File Coverage

blib/lib/Type/Params/Signature.pm
Criterion Covered Total %
statement 433 446 97.0
branch 201 224 89.7
condition 90 119 75.6
subroutine 69 71 97.1
pod 0 37 0.0
total 793 897 88.4


line stmt bran cond sub pod time code
1             # INTERNAL MODULE: OO backend for Type::Params signatures.
2              
3             package Type::Params::Signature;
4              
5 51     51   1897 use 5.008001;
  51         200  
6 51     51   330 use strict;
  51         137  
  51         1172  
7 51     51   307 use warnings;
  51         117  
  51         2397  
8              
9             BEGIN {
10 51 50   51   2035 if ( $] < 5.010 ) { require Devel::TypeTiny::Perl58Compat }
  0         0  
11             }
12              
13             BEGIN {
14 51     51   195 $Type::Params::Signature::AUTHORITY = 'cpan:TOBYINK';
15 51         2132 $Type::Params::Signature::VERSION = '2.002001';
16             }
17              
18             $Type::Params::Signature::VERSION =~ tr/_//d;
19              
20 51     51   366 use B ();
  51         136  
  51         1313  
21 51     51   24525 use Eval::TypeTiny::CodeAccumulator;
  51         145  
  51         1875  
22 51     51   368 use Types::Standard qw( -is -types -assert );
  51         146  
  51         710  
23 51     51   8082 use Types::TypeTiny qw( -is -types to_TypeTiny );
  51         140  
  51         449  
24 51     51   109494 use Type::Params::Parameter;
  51         173  
  51         28989  
25              
26             sub _croak {
27 13     13   70 require Error::TypeTiny;
28 13         60 return Error::TypeTiny::croak( pop );
29             }
30              
31             sub _new_parameter {
32 704     704   1442 shift;
33 704         2321 'Type::Params::Parameter'->new( @_ );
34             }
35              
36             sub _new_code_accumulator {
37 310     310   598 shift;
38 310         1913 'Eval::TypeTiny::CodeAccumulator'->new( @_ );
39             }
40              
41             sub new {
42 287     287 0 574 my $class = shift;
43 287 50       1584 my %self = @_ == 1 ? %{$_[0]} : @_;
  0         0  
44 287         703 my $self = bless \%self, $class;
45 287   50     1134 $self->{parameters} ||= [];
46 287   50     1470 $self->{class_prefix} ||= 'Type::Params::OO::Klass';
47 287         951 $self->BUILD;
48 280         906 return $self;
49             }
50              
51             {
52             my $klass_id;
53             my %klass_cache;
54             sub BUILD {
55 287     287 0 521 my $self = shift;
56              
57 287 100 100     1007 if ( $self->{named_to_list} and not ref $self->{named_to_list} ) {
58 8         21 $self->{named_to_list} = [ map $_->name, @{ $self->{parameters} } ];
  8         37  
59             }
60              
61 287 50       862 if ( delete $self->{rationalize_slurpies} ) {
62 287         1245 $self->_rationalize_slurpies;
63             }
64              
65 282 100       947 if ( $self->{method} ) {
66 26         60 my $type = $self->{method};
67             $type =
68             is_Int($type) ? Defined :
69 26 0       189 is_Str($type) ? do { require Type::Utils; Type::Utils::dwim_type( $type, $self->{package} ? ( for => $self->{package} ) : () ) } :
  0 50       0  
  0 100       0  
70             to_TypeTiny( $type );
71 26   50     117 unshift @{ $self->{head} ||= [] }, $self->_new_parameter(
  26         173  
72             name => 'invocant',
73             type => $type,
74             );
75             }
76              
77 282 100 100     1562 if ( defined $self->{bless} and $self->{bless} eq 1 and not $self->{named_to_list} ) {
      100        
78 30         110 my $klass_key = $self->_klass_key;
79 30   66     1574 $self->{bless} = ( $klass_cache{$klass_key} ||= sprintf( '%s%d', $self->{class_prefix}, ++$klass_id ) );
80 30 50       109 $self->{oo_trace} = 1 unless exists $self->{oo_trace};
81 30         110 $self->make_class;
82             }
83 280 100       7406 if ( is_ArrayRef $self->{class} ) {
84 8         29 $self->{constructor} = $self->{class}->[1];
85 8         25 $self->{class} = $self->{class}->[0];
86             }
87             }
88             }
89              
90             sub _klass_key {
91 30     30   70 my $self = shift;
92              
93 30         65 my @parameters = @{ $self->parameters };
  30         93  
94 30 100       97 if ( $self->has_slurpy ) {
95 1         3 push @parameters, $self->slurpy;
96             }
97              
98 51     51   576 no warnings 'uninitialized';
  51         138  
  51         283978  
99             join(
100             '|',
101             map sprintf( '%s*%s*%s', $_->name, $_->getter, $_->predicate ),
102 30         180 sort { $a->{name} cmp $b->{name} } @parameters
  44         228  
103             );
104             }
105              
106             sub _rationalize_slurpies {
107 287     287   525 my $self = shift;
108              
109 287         750 my $parameters = $self->parameters;
110              
111 287 100       786 if ( $self->is_named ) {
    100          
112 154         310 my ( @slurpy, @rest );
113              
114 154         370 for my $parameter ( @$parameters ) {
115 378 100       1159 if ( $parameter->type->is_strictly_a_type_of( Slurpy ) ) {
    100          
116 26         154 push @slurpy, $parameter;
117             }
118             elsif ( $parameter->{slurpy} ) {
119 1         4 $parameter->{type} = Slurpy[ $parameter->type ];
120 1         7 push @slurpy, $parameter;
121             }
122             else {
123 351         993 push @rest, $parameter;
124             }
125             }
126              
127 154 100       752 if ( @slurpy == 1 ) {
    100          
128 25         94 my $constraint = $slurpy[0]->type;
129 25 100 66     101 if ( $constraint->type_parameter && $constraint->type_parameter->{uniq} == Any->{uniq} or $constraint->my_slurp_into eq 'HASH' ) {
      100        
130 24         85 $self->{slurpy} = $slurpy[0];
131 24         99 @$parameters = @rest;
132             }
133             else {
134 1         4 $self->_croak( 'Signatures with named parameters can only have slurpy parameters which are a subtype of HashRef' );
135             }
136             }
137             elsif ( @slurpy ) {
138 1         3 $self->_croak( 'Found multiple slurpy parameters! There can be only one' );
139             }
140             }
141             elsif ( @$parameters ) {
142 131 100       449 if ( $parameters->[-1]->type->is_strictly_a_type_of( Slurpy ) ) {
    100          
143 40         126 $self->{slurpy} = pop @$parameters;
144             }
145             elsif ( $parameters->[-1]{slurpy} ) {
146 6         16 $self->{slurpy} = pop @$parameters;
147 6         23 $self->{slurpy}{type} = Slurpy[ $self->{slurpy}{type} ];
148             }
149              
150 131         405 for my $parameter ( @$parameters ) {
151 179 100 66     544 if ( $parameter->type->is_strictly_a_type_of( Slurpy ) or $parameter->{slurpy} ) {
152 3         12 $self->_croak( 'Parameter following slurpy parameter' );
153             }
154             }
155             }
156              
157 282 100 100     1431 if ( $self->{slurpy} and $self->{slurpy}->has_default ) {
158 1         7 require Carp;
159 1         3 our @CARP_NOT = ( __PACKAGE__, 'Type::Params' );
160 1         230 Carp::carp( "Warning: the default for the slurpy parameter will be ignored, continuing anyway" );
161 1         24 delete $self->{slurpy}{default};
162             }
163             }
164              
165             sub _parameters_from_list {
166 326     326   1328 my ( $class, $style, $list, %opts ) = @_;
167 326         1035 my @return;
168 326         626 my $is_named = ( $style eq 'named' );
169              
170 326         1028 while ( @$list ) {
171 678         1111 my ( $type, %param_opts );
172 678 100       1421 if ( $is_named ) {
173 378         1150 $param_opts{name} = assert_Str( shift( @$list ) );
174             }
175 678 100 66     3817 if ( is_HashRef $list->[0] and exists $list->[0]{slurpy} and not is_Bool $list->[0]{slurpy} ) {
      33        
176 2         3 my %new_opts = %{ shift( @$list ) };
  2         10  
177 2         4 $type = delete $new_opts{slurpy};
178 2         7 %param_opts = ( %param_opts, %new_opts, slurpy => 1 );
179             }
180             else {
181 676         1229 $type = shift( @$list );
182             }
183 678 100       2176 if ( is_HashRef( $list->[0] ) ) {
184 57 100 100     263 unless ( exists $list->[0]{slurpy} and not is_Bool $list->[0]{slurpy} ) {
185 55         102 %param_opts = ( %param_opts, %{ +shift( @$list ) } );
  55         211  
186             }
187             }
188             $param_opts{type} =
189 4         11 is_Int($type) ? ( $type ? Any : do { $param_opts{optional} = !!1; Any; } ) :
  4         17  
190 678 100       3243 is_Str($type) ? do { require Type::Utils; Type::Utils::dwim_type( $type, $opts{package} ? ( for => $opts{package} ) : () ) } :
  0 0       0  
  0 50       0  
    100          
191             to_TypeTiny( $type );
192 678         2295 my $parameter = $class->_new_parameter( %param_opts );
193 678         2316 push @return, $parameter;
194             }
195              
196 326         1227 return \@return;
197             }
198              
199             sub new_from_compile {
200 287     287 0 714 my $class = shift;
201 287         521 my $style = shift;
202 287         671 my $is_named = ( $style eq 'named' );
203              
204 287         563 my %opts = ();
205 287   66     1937 while ( is_HashRef $_[0] and not exists $_[0]{slurpy} ) {
206 379         855 %opts = ( %opts, %{ +shift } );
  379         2514  
207             }
208              
209 287         787 for my $pos ( qw/ head tail / ) {
210 574 100       1573 next unless defined $opts{$pos};
211 39 100       113 if ( is_Int( $opts{$pos} ) ) {
212 6         18 $opts{$pos} = [ ( Any ) x $opts{$pos} ];
213             }
214 39         147 $opts{$pos} = $class->_parameters_from_list( positional => $opts{$pos}, %opts );
215             }
216              
217 287         794 my $list = [ @_ ];
218 287         687 $opts{is_named} = $is_named;
219 287         1167 $opts{parameters} = $class->_parameters_from_list( $style => $list, %opts );
220              
221 287         1194 my $self = $class->new( %opts, rationalize_slurpies => 1 );
222 280         1796 return $self;
223             }
224              
225             sub new_from_v2api {
226 308     308 0 863 my ( $class, $opts ) = @_;
227              
228 308   100     1486 my $positional = delete( $opts->{positional} ) || delete( $opts->{pos} );
229 308         644 my $named = delete( $opts->{named} );
230 308   100     1218 my $multiple = delete( $opts->{multiple} ) || delete( $opts->{multi} );
231              
232 308 100 100     1595 $class->_croak( "Signature must be positional, named, or multiple" )
      100        
233             unless $positional || $named || $multiple;
234              
235 306 100       902 if ( $multiple ) {
236 18 100       73 $multiple = [] unless is_ArrayRef $multiple;
237 18 100       67 unshift @$multiple, { positional => $positional } if $positional;
238 18 100       45 unshift @$multiple, { named => $named } if $named;
239 18         1667 require Type::Params::Alternatives;
240 18         123 return 'Type::Params::Alternatives'->new(
241             base_options => $opts,
242             alternatives => $multiple,
243             sig_class => $class,
244             );
245             }
246              
247 288         736 my ( $sig_kind, $args ) = ( pos => $positional );
248 288 100       902 if ( $named ) {
249 155 100       476 $opts->{bless} = 1 unless exists $opts->{bless};
250 155         376 ( $sig_kind, $args ) = ( named => $named );
251 155 100       423 $class->_croak( "Signature cannot have both positional and named arguments" )
252             if $positional;
253             }
254              
255 286         962 return $class->new_from_compile( $sig_kind, $opts, @$args );
256             }
257              
258 304     304 0 1183 sub package { $_[0]{package} }
259 304     304 0 2231 sub subname { $_[0]{subname} }
260 297     297 0 1252 sub description { $_[0]{description} } sub has_description { exists $_[0]{description} }
  0     0 0 0  
261 297     297 0 943 sub method { $_[0]{method} }
262 1359     1359 0 3815 sub head { $_[0]{head} } sub has_head { exists $_[0]{head} }
  327     327 0 2468  
263 1181     1181 0 3153 sub tail { $_[0]{tail} } sub has_tail { exists $_[0]{tail} }
  64     64 0 134  
264 1     1 0 457 sub parameters { $_[0]{parameters} } sub has_parameters { exists $_[0]{parameters} }
  1043     1043 0 3254  
265 700     700 0 2287 sub slurpy { $_[0]{slurpy} } sub has_slurpy { exists $_[0]{slurpy} }
  218     218 0 748  
266 1686     1686 0 9478 sub on_die { $_[0]{on_die} } sub has_on_die { exists $_[0]{on_die} }
  6     6 0 22  
267 1087     1087 0 3096 sub strictness { $_[0]{strictness} } sub has_strictness { exists $_[0]{strictness} }
  699     699 0 2985  
268 575     575 0 2733 sub goto_next { $_[0]{goto_next} }
269 1909     1909 0 6192 sub is_named { $_[0]{is_named} }
270 504     504 0 1909 sub bless { $_[0]{bless} }
271 163     163 0 596 sub class { $_[0]{class} }
272 24     24 0 159 sub constructor { $_[0]{constructor} }
273 177     177 0 845 sub named_to_list { $_[0]{named_to_list} }
274 43     43 0 168 sub oo_trace { $_[0]{oo_trace} }
275              
276 38 100   38 0 276 sub method_invocant { $_[0]{method_invocant} = defined( $_[0]{method_invocant} ) ? $_[0]{method_invocant} : 'undef' }
277              
278             sub can_shortcut {
279             return $_[0]{can_shortcut}
280 440 100   440 0 1457 if exists $_[0]{can_shortcut};
281             $_[0]{can_shortcut} = !(
282             $_[0]->slurpy or
283 147   100     442 grep $_->might_supply_new_value, @{ $_[0]->parameters }
284             );
285             }
286              
287             sub coderef {
288 297   66 297 0 1463 $_[0]{coderef} ||= $_[0]->_build_coderef;
289             }
290              
291             sub _build_coderef {
292 297     297   555 my $self = shift;
293 297   66     898 my $coderef = $self->_new_code_accumulator(
294             description => $self->description
295             || sprintf( q{parameter validation for '%s::%s'}, $self->package || '', $self->subname || '__ANON__' )
296             );
297              
298 297         1221 $self->_coderef_start( $coderef );
299 295 100       770 $self->_coderef_head( $coderef ) if $self->has_head;
300 295 100       869 $self->_coderef_tail( $coderef ) if $self->has_tail;
301 295         1093 $self->_coderef_parameters( $coderef );
302 294 100       888 if ( $self->has_slurpy ) {
    100          
303 69         262 $self->_coderef_slurpy( $coderef );
304             }
305             elsif ( $self->is_named ) {
306 125         429 $self->_coderef_extra_names( $coderef );
307             }
308 294         1148 $self->_coderef_end( $coderef );
309              
310 294         1074 return $coderef;
311             }
312              
313             sub _coderef_start {
314 297     297   827 my ( $self, $coderef ) = ( shift, @_ );
315              
316 297         1137 $coderef->add_line( 'sub {' );
317 297         1520 $coderef->{indent} .= "\t";
318              
319 297 100       859 if ( my $next = $self->goto_next ) {
320 31 100       564 if ( is_CodeLike $next ) {
321 30         125 $coderef->add_variable( '$__NEXT__', \$next );
322             }
323             else {
324 1         3 $coderef->add_line( 'my $__NEXT__ = shift;' );
325 1         4 $coderef->add_gap;
326             }
327             }
328              
329 297 100       893 if ( $self->method ) {
330             # Passed to parameter defaults
331 34         149 $self->{method_invocant} = '$__INVOCANT__';
332 34         95 $coderef->add_line( sprintf 'my %s = $_[0];', $self->method_invocant );
333 34         535 $coderef->add_gap;
334             }
335              
336 297         931 $self->_coderef_start_extra( $coderef );
337              
338 296         519 my $extravars = '';
339 296 100       901 if ( $self->has_head ) {
340 48         96 $extravars .= ', @head';
341             }
342 296 100       835 if ( $self->has_tail ) {
343 16         29 $extravars .= ', @tail';
344             }
345              
346 296 100       795 if ( $self->is_named ) {
    100          
347 149         582 $coderef->add_line( "my ( \%out, \%in, \%tmp, \$tmp, \$dtmp$extravars );" );
348             }
349             elsif ( $self->can_shortcut ) {
350 84         373 $coderef->add_line( "my ( \%tmp, \$tmp$extravars );" );
351             }
352             else {
353 63         266 $coderef->add_line( "my ( \@out, \%tmp, \$tmp, \$dtmp$extravars );" );
354             }
355              
356 296 100       950 if ( $self->has_on_die ) {
357 6         23 $coderef->add_variable( '$__ON_DIE__', \ $self->on_die );
358             }
359              
360 296         1147 $coderef->add_gap;
361              
362 296         1028 $self->_coderef_check_count( $coderef );
363              
364 295         1145 $coderef->add_gap;
365              
366 295         585 $self;
367             }
368              
369       279     sub _coderef_start_extra {}
370              
371             sub _coderef_check_count {
372 279     279   687 my ( $self, $coderef ) = ( shift, @_ );
373              
374 279         538 my $strictness_test = '';
375 279 100 100     691 if ( defined $self->strictness and $self->strictness eq 1 ) {
    100          
    100          
376 1         2 $strictness_test = '';
377             }
378             elsif ( $self->strictness ) {
379 3         7 $strictness_test = sprintf '( not %s ) or ', $self->strictness;
380             }
381             elsif ( $self->has_strictness ) {
382 1         3 return $self;
383             }
384              
385 278         582 my $headtail = 0;
386 278 100       628 $headtail += @{ $self->head } if $self->has_head;
  48         108  
387 278 100       674 $headtail += @{ $self->tail } if $self->has_tail;
  16         40  
388              
389 278         593 my $is_named = $self->is_named;
390 278         521 my $min_args = 0;
391 278         471 my $max_args = 0;
392 278         455 my $seen_optional = 0;
393 278         459 for my $parameter ( @{ $self->parameters } ) {
  278         578  
394 521 100       1426 if ( $parameter->optional ) {
395 114         1803 ++$seen_optional;
396 114         264 ++$max_args;
397             }
398             else {
399 407 100 100     18078 $seen_optional and !$is_named and $self->_croak(
400             'Non-Optional parameter following Optional parameter',
401             );
402 406         642 ++$max_args;
403 406         781 ++$min_args;
404             }
405             }
406              
407 277 100       864 undef $max_args if $self->has_slurpy;
408              
409             # Note: code related to $max_args_if_hash is currently commented out
410             # because it handles this badly:
411             #
412             # my %opts = ( x => 1, y => 1 );
413             # your_func( %opts, y => 2 ); # override y
414             #
415              
416 277 100       729 if ( $is_named ) {
417 149         367 my $args_if_hashref = $headtail + 1;
418 149 100       233 my $hashref_index = @{ $self->head || [] };
  149         383  
419 149         373 my $arity_if_hash = $headtail % 2;
420 149         376 my $min_args_if_hash = $headtail + ( 2 * $min_args );
421             #my $max_args_if_hash = defined( $max_args )
422             # ? ( $headtail + ( 2 * $max_args ) )
423             # : undef;
424              
425 149         906 require List::Util;
426 149         756 $self->{min_args} = List::Util::min( $args_if_hashref, $min_args_if_hash );
427             #if ( defined $max_args_if_hash ) {
428             # $self->{max_args} = List::Util::max( $args_if_hashref, $max_args_if_hash );
429             #}
430              
431 149         306 my $extra_conditions = '';
432             #if ( defined $max_args_if_hash and $min_args_if_hash==$max_args_if_hash ) {
433             # $extra_conditions .= " && \@_ == $min_args_if_hash"
434             #}
435             #else {
436 149 100       511 $extra_conditions .= " && \@_ >= $min_args_if_hash"
437             if $min_args_if_hash;
438             # $extra_conditions .= " && \@_ <= $max_args_if_hash"
439             # if defined $max_args_if_hash;
440             #}
441              
442 149         602 $coderef->add_line( $strictness_test . sprintf(
443             "\@_ == %d && %s\n\tor \@_ %% 2 == %d%s\n\tor %s;",
444             $args_if_hashref,
445             HashRef->inline_check( sprintf '$_[%d]', $hashref_index ),
446             $arity_if_hash,
447             $extra_conditions,
448             $self->_make_count_fail(
449             coderef => $coderef,
450             got => 'scalar( @_ )',
451             ),
452             ) );
453             }
454             else {
455 128         254 $min_args += $headtail;
456 128 100       401 $max_args += $headtail if defined $max_args;
457              
458 128         299 $self->{min_args} = $min_args;
459 128         278 $self->{max_args} = $max_args;
460              
461 128 100 100     698 if ( defined $max_args and $min_args == $max_args ) {
    100 100        
462 67         244 $coderef->add_line( $strictness_test . sprintf(
463             "\@_ == %d\n\tor %s;",
464             $min_args,
465             $self->_make_count_fail(
466             coderef => $coderef,
467             minimum => $min_args,
468             maximum => $max_args,
469             got => 'scalar( @_ )',
470             ),
471             ) );
472             }
473             elsif ( $min_args and defined $max_args ) {
474 8         25 $coderef->add_line( $strictness_test . sprintf(
475             "\@_ >= %d && \@_ <= %d\n\tor %s;",
476             $min_args,
477             $max_args,
478             $self->_make_count_fail(
479             coderef => $coderef,
480             minimum => $min_args,
481             maximum => $max_args,
482             got => 'scalar( @_ )',
483             ),
484             ) );
485             }
486             else {
487 53   100     362 $coderef->add_line( $strictness_test . sprintf(
      100        
488             "\@_ >= %d\n\tor %s;",
489             $min_args || 0,
490             $self->_make_count_fail(
491             coderef => $coderef,
492             minimum => $min_args || 0,
493             got => 'scalar( @_ )',
494             ),
495             ) );
496             }
497             }
498             }
499              
500             sub _coderef_head {
501 48     48   121 my ( $self, $coderef ) = ( shift, @_ );
502 48 50       109 $self->has_head or return;
503              
504 48         100 my $size = @{ $self->head };
  48         105  
505 48         211 $coderef->add_line( sprintf(
506             '@head = splice( @_, 0, %d );',
507             $size,
508             ) );
509              
510 48         144 $coderef->add_gap;
511              
512 48         84 my $i = 0;
513 48         77 for my $parameter ( @{ $self->head } ) {
  48         112  
514 56         366 $parameter->_make_code(
515             signature => $self,
516             coderef => $coderef,
517             input_slot => sprintf( '$head[%d]', $i ),
518             input_var => '@head',
519             output_slot => sprintf( '$head[%d]', $i ),
520             output_var => undef,
521             index => $i,
522             type => 'head',
523             display_var => sprintf( '$_[%d]', $i ),
524             );
525 56         136 ++$i;
526             }
527              
528 48         98 $self;
529             }
530              
531             sub _coderef_tail {
532 16     16   38 my ( $self, $coderef ) = ( shift, @_ );
533 16 50       35 $self->has_tail or return;
534              
535 16         27 my $size = @{ $self->tail };
  16         32  
536 16         73 $coderef->add_line( sprintf(
537             '@tail = splice( @_, -%d );',
538             $size,
539             ) );
540              
541 16         50 $coderef->add_gap;
542              
543 16         27 my $i = 0;
544 16         24 my $n = @{ $self->tail };
  16         31  
545 16         53 for my $parameter ( @{ $self->tail } ) {
  16         34  
546 42         249 $parameter->_make_code(
547             signature => $self,
548             coderef => $coderef,
549             input_slot => sprintf( '$tail[%d]', $i ),
550             input_var => '@tail',
551             output_slot => sprintf( '$tail[%d]', $i ),
552             output_var => undef,
553             index => $i,
554             type => 'tail',
555             display_var => sprintf( '$_[-%d]', $n - $i ),
556             );
557 42         101 ++$i;
558             }
559              
560 16         35 $self;
561             }
562              
563             sub _coderef_parameters {
564 295     295   760 my ( $self, $coderef ) = ( shift, @_ );
565              
566 295 100       743 if ( $self->is_named ) {
567              
568 149         509 $coderef->add_line( sprintf(
569             '%%in = ( @_ == 1 and %s ) ? %%{ $_[0] } : @_;',
570             HashRef->inline_check( '$_[0]' ),
571             ) );
572              
573 149         535 $coderef->add_gap;
574              
575 149         258 for my $parameter ( @{ $self->parameters } ) {
  149         352  
576 347         1055 my $qname = B::perlstring( $parameter->name );
577 347         1511 $parameter->_make_code(
578             signature => $self,
579             coderef => $coderef,
580             is_named => 1,
581             input_slot => sprintf( '$in{%s}', $qname ),
582             output_slot => sprintf( '$out{%s}', $qname ),
583             display_var => sprintf( '$_{%s}', $qname ),
584             key => $parameter->name,
585             type => 'named_arg',
586             );
587             }
588             }
589             else {
590 146         402 my $can_shortcut = $self->can_shortcut;
591 146 100       360 my $head_size = $self->has_head ? @{ $self->head } : 0;
  30         80  
592              
593 146         255 my $i = 0;
594 146         256 for my $parameter ( @{ $self->parameters } ) {
  146         362  
595 174 100       1351 $parameter->_make_code(
    100          
596             signature => $self,
597             coderef => $coderef,
598             is_named => 0,
599             input_slot => sprintf( '$_[%d]', $i ),
600             input_var => '@_',
601             output_slot => ( $can_shortcut ? undef : sprintf( '$_[%d]', $i ) ),
602             output_var => ( $can_shortcut ? undef : '@out' ),
603             index => $i,
604             display_var => sprintf( '$_[%d]', $i + $head_size ),
605             );
606 173         491 ++$i;
607             }
608             }
609             }
610              
611             sub _coderef_slurpy {
612 69     69   189 my ( $self, $coderef ) = ( shift, @_ );
613 69 50       173 return unless $self->has_slurpy;
614              
615 69         240 my $parameter = $self->slurpy;
616 69         225 my $constraint = $parameter->type;
617 69         590 my $slurp_into = $constraint->my_slurp_into;
618 69         501 my $real_type = $constraint->my_unslurpy;
619              
620 69 100 66     248 if ( $self->is_named ) {
    100          
    100          
621 24         85 $coderef->add_line( 'my $SLURPY = \\%in;' );
622             }
623             elsif ( $real_type and $real_type->{uniq} == Any->{uniq} ) {
624              
625             $coderef->add_line( sprintf(
626             'my $SLURPY = [ @_[ %d .. $#_ ] ];',
627 1         7 scalar( @{ $self->parameters } ),
  1         2  
628             ) );
629             }
630             elsif ( $slurp_into eq 'HASH' ) {
631              
632 29         208 my $index = scalar( @{ $self->parameters } );
  29         84  
633 29   33     97 $coderef->add_line( sprintf(
634             'my $SLURPY = ( $#_ == %d and ( %s ) ) ? { %%{ $_[%d] } } : ( ( $#_ - %d ) %% 2 ) ? { @_[ %d .. $#_ ] } : %s;',
635             $index,
636             HashRef->inline_check("\$_[$index]"),
637             $index,
638             $index,
639             $index,
640             $self->_make_general_fail(
641             coderef => $coderef,
642             message => sprintf(
643             qq{sprintf( "Odd number of elements in %%s", %s )},
644             B::perlstring( ( $real_type or $constraint )->display_name ),
645             ),
646             ),
647             ) );
648             }
649             else {
650            
651             $coderef->add_line( sprintf(
652             'my $SLURPY = [ @_[ %d .. $#_ ] ];',
653 15         116 scalar( @{ $self->parameters } ),
  15         39  
654             ) );
655             }
656              
657 69         345 $coderef->add_gap;
658              
659 69 100       262 $parameter->_make_code(
660             signature => $self,
661             coderef => $coderef,
662             input_slot => '$SLURPY',
663             display_var => '$SLURPY',
664             index => 0,
665             $self->is_named
666             ? ( output_slot => sprintf( '$out{%s}', B::perlstring( $parameter->name ) ) )
667             : ( output_var => '@out' )
668             );
669             }
670              
671             sub _coderef_extra_names {
672 125     125   325 my ( $self, $coderef ) = ( shift, @_ );
673              
674 125 50 33     284 return $self if $self->has_strictness && ! $self->strictness;
675              
676 125         7786 require Type::Utils;
677 125         300 my $english_list = 'Type::Utils::english_list';
678 125 100       381 if ( $Type::Tiny::AvoidCallbacks ) {
679 8         37 $english_list = 'join q{, } => ';
680             }
681              
682 125         1971 $coderef->add_line( '# Unrecognized parameters' );
683 125 50 33     572 $coderef->add_line( sprintf(
684             '%s if %skeys %%in;',
685             $self->_make_general_fail(
686             coderef => $coderef,
687             message => "sprintf( q{Unrecognized parameter%s: %s}, keys( %in ) > 1 ? q{s} : q{}, $english_list( sort keys %in ) )",
688             ),
689             defined( $self->strictness ) && $self->strictness ne 1
690             ? sprintf( '%s && ', $self->strictness )
691             : ''
692             ) );
693 125         362 $coderef->add_gap;
694             }
695              
696             sub _coderef_end {
697 294     294   757 my ( $self, $coderef ) = ( shift, @_ );
698              
699 294 100 100     742 if ( $self->bless and $self->oo_trace ) {
700 28         84 my $package = $self->package;
701 28         77 my $subname = $self->subname;
702 28 50 33     136 if ( defined $package and defined $subname ) {
703 28         217 $coderef->add_line( sprintf(
704             '$out{"~~caller"} = %s;',
705             B::perlstring( "$package\::$subname" ),
706             ) );
707 28         102 $coderef->add_gap;
708             }
709             }
710              
711 294         1006 $self->_coderef_end_extra( $coderef );
712 294         875 $coderef->add_line( $self->_make_return_expression( is_early => 0 ) . ';' );
713 294         1702 $coderef->{indent} =~ s/\t$//;
714 294         1025 $coderef->add_line( '}' );
715              
716 294         536 $self;
717             }
718              
719       277     sub _coderef_end_extra {}
720              
721             sub _make_return_list {
722 296     296   517 my $self = shift;
723              
724 296         486 my @return_list;
725 296 100       680 if ( $self->has_head ) {
726 48         120 push @return_list, '@head';
727             }
728              
729 296 100       749 if ( not $self->is_named ) {
    100          
    100          
    100          
730 147 100       503 push @return_list, $self->can_shortcut ? '@_' : '@out';
731             }
732             elsif ( $self->named_to_list ) {
733             push @return_list, map(
734             sprintf( '$out{%s}', B::perlstring( $_ ) ),
735 10         21 @{ $self->named_to_list },
  10         20  
736             );
737             }
738             elsif ( $self->class ) {
739 24   100     71 push @return_list, sprintf(
740             '%s->%s( \%%out )',
741             B::perlstring( $self->class ),
742             $self->constructor || 'new',
743             );
744             }
745             elsif ( $self->bless ) {
746 36         97 push @return_list, sprintf(
747             'bless( \%%out, %s )',
748             B::perlstring( $self->bless ),
749             );
750             }
751             else {
752 79         166 push @return_list, '\%out';
753             }
754              
755 296 100       801 if ( $self->has_tail ) {
756 16         34 push @return_list, '@tail';
757             }
758              
759 296         918 return @return_list;
760             }
761              
762             sub _make_return_expression {
763 313     313   926 my ( $self, %args ) = @_;
764              
765 313         958 my $list = join q{, }, $self->_make_return_list;
766              
767 313 100 66     816 if ( $self->goto_next ) {
    100          
768 31 100       155 if ( $list eq '@_' ) {
769 7         40 return sprintf 'goto( $__NEXT__ )';
770             }
771             else {
772 24         166 return sprintf 'do { @_ = ( %s ); goto $__NEXT__ }',
773             $list;
774             }
775             }
776             elsif ( $args{is_early} or not exists $args{is_early} ) {
777 19         108 return sprintf 'return( %s )', $list;
778             }
779             else {
780 263         1684 return sprintf '( %s )', $list;
781             }
782             }
783              
784             sub _make_general_fail {
785 441     441   1450 my ( $self, %args ) = ( shift, @_ );
786              
787             return sprintf(
788             $self->has_on_die
789             ? q{return( "Error::TypeTiny"->throw_cb( $__ON_DIE__, message => %s ) )}
790             : q{"Error::TypeTiny"->throw( message => %s )},
791             $args{message},
792 441 100       1031 );
793             }
794              
795             sub _make_constraint_fail {
796 672     672   3394 my ( $self, %args ) = ( shift, @_ );
797              
798             return sprintf(
799             $self->has_on_die
800             ? q{return( Type::Tiny::_failed_check( %d, %s, %s, varname => %s, on_die => $__ON_DIE__ ) )}
801             : q{Type::Tiny::_failed_check( %d, %s, %s, varname => %s )},
802             $args{constraint}{uniq},
803             B::perlstring( $args{constraint}->display_name ),
804             $args{varname},
805 672 100 33     1815 B::perlstring( $args{display_var} || $args{varname} ),
806             );
807             }
808              
809             sub _make_count_fail {
810 277     277   1219 my ( $self, %args ) = ( shift, @_ );
811              
812 277         557 my @counts;
813 277 50       816 if ( $args{got} ) {
814             push @counts, sprintf(
815             'got => %s',
816             $args{got},
817 277         1123 );
818             }
819 277         669 for my $c ( qw/ minimum maximum / ) {
820 554 100       2479 is_Int( $args{$c} ) or next;
821             push @counts, sprintf(
822             '%s => %s',
823             $c,
824 203         726 $args{$c},
825             );
826             }
827              
828 277 100       843 return sprintf(
829             $self->has_on_die
830             ? q{return( "Error::TypeTiny::WrongNumberOfParameters"->throw_cb( $__ON_DIE__, %s ) )}
831             : q{"Error::TypeTiny::WrongNumberOfParameters"->throw( %s )},
832             join( q{, }, @counts ),
833             );
834             }
835              
836             sub class_attributes {
837 43     43 0 92 my $self = shift;
838 43   66     198 $self->{class_attributes} ||= $self->_build_class_attributes;
839             }
840              
841             sub _build_class_attributes {
842 30     30   56 my $self = shift;
843 30         63 my %predicates;
844             my %getters;
845              
846 30         63 my @parameters = @{ $self->parameters };
  30         79  
847 30 100       100 if ( $self->has_slurpy ) {
848 1         12 push @parameters, $self->slurpy;
849             }
850              
851 30         91 for my $parameter ( @parameters ) {
852              
853 63         172 my $name = $parameter->name;
854 63 100       335 if ( my $predicate = $parameter->predicate ) {
855 20 50       96 $predicate =~ /^[^0-9\W]\w*$/
856             or $self->_croak( "Bad accessor name: \"$predicate\"" );
857 20         58 $predicates{$predicate} = $name;
858             }
859 63 50       160 if ( my $getter = $parameter->getter ) {
860 63 100       315 $getter =~ /^[^0-9\W]\w*$/
861             or $self->_croak( "Bad accessor name: \"$getter\"" );
862 61         206 $getters{$getter} = $name;
863             }
864             }
865              
866             return {
867 28         210 exists_predicates => \%predicates,
868             getters => \%getters,
869             };
870             }
871              
872             sub make_class {
873 30     30 0 67 my $self = shift;
874            
875 30   50     189 my $env = uc( $ENV{PERL_TYPE_PARAMS_XS} || 'XS' );
876 30 50 33     198 if ( $env eq 'PP' or $ENV{PERL_ONLY} ) {
877 0         0 $self->make_class_pp;
878             }
879              
880 30         88 $self->make_class_xs;
881             }
882              
883             sub make_class_xs {
884 30     30 0 68 my $self = shift;
885              
886 30 50       68 eval {
887 30         5069 require Class::XSAccessor;
888 30         20811 'Class::XSAccessor'->VERSION( '1.17' );
889 30         178 1;
890             } or return $self->make_class_pp;
891              
892 30         104 my $attr = $self->class_attributes;
893              
894 28         106 'Class::XSAccessor'->import(
895             class => $self->bless,
896             replace => 1,
897             %$attr,
898             );
899             }
900              
901             sub make_class_pp {
902 0     0 0 0 my $self = shift;
903              
904 0         0 my $code = $self->make_class_pp_code;
905 0         0 do {
906 0         0 local $@;
907 0 0       0 eval( $code ) or die( $@ );
908             };
909             }
910              
911             sub make_class_pp_code {
912 45     45 0 98 my $self = shift;
913              
914 45 100 66     104 return ''
      100        
915             unless $self->is_named && $self->bless && !$self->named_to_list;
916              
917 13         42 my $coderef = $self->_new_code_accumulator;
918 13         39 my $attr = $self->class_attributes;
919              
920 13         53 $coderef->add_line( '{' );
921 13         36 $coderef->{indent} = "\t";
922 13         35 $coderef->add_line( sprintf( 'package %s;', $self->bless ) );
923 13         42 $coderef->add_line( 'use strict;' );
924 13         40 $coderef->add_line( 'no warnings;' );
925              
926 13         24 for my $function ( sort keys %{ $attr->{getters} } ) {
  13         73  
927 28         85 my $slot = $attr->{getters}{$function};
928 28         141 $coderef->add_line( sprintf(
929             'sub %s { $_[0]{%s} }',
930             $function,
931             B::perlstring( $slot ),
932             ) );
933             }
934              
935 13         30 for my $function ( sort keys %{ $attr->{exists_predicates} } ) {
  13         43  
936 12         27 my $slot = $attr->{exists_predicates}{$function};
937 12         55 $coderef->add_line( sprintf(
938             'sub %s { exists $_[0]{%s} }',
939             $function,
940             B::perlstring( $slot ),
941             ) );
942             }
943            
944 13         45 $coderef->add_line( '1;' );
945 13         26 $coderef->{indent} = "";
946 13         40 $coderef->add_line( '}' );
947              
948 13         38 return $coderef->code;
949             }
950              
951             sub return_wanted {
952 288     288 0 530 my $self = shift;
953 288         845 my $coderef = $self->coderef;
954              
955 285 100       1410 if ( $self->{want_source} ) {
    100          
    100          
956 7         24 return $coderef->code;
957             }
958             elsif ( $self->{want_object} ) { # undocumented for now
959 1         6 return $self;
960             }
961             elsif ( $self->{want_details} ) {
962             return {
963             min_args => $self->{min_args},
964             max_args => $self->{max_args},
965             environment => $coderef->{env},
966 49         179 source => $coderef->code,
967             closure => $coderef->compile,
968             named => $self->is_named,
969             class_definition => $self->make_class_pp_code,
970             };
971             }
972              
973 228         917 return $coderef->compile;
974             }
975              
976             1;