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 52     52   1651 use 5.008001;
  52         206  
6 52     52   327 use strict;
  52         112  
  52         1135  
7 52     52   276 use warnings;
  52         122  
  52         2341  
8              
9             BEGIN {
10 52 50   52   1968 if ( $] < 5.010 ) { require Devel::TypeTiny::Perl58Compat }
  0         0  
11             }
12              
13             BEGIN {
14 52     52   180 $Type::Params::Signature::AUTHORITY = 'cpan:TOBYINK';
15 52         2026 $Type::Params::Signature::VERSION = '2.004000';
16             }
17              
18             $Type::Params::Signature::VERSION =~ tr/_//d;
19              
20 52     52   505 use B ();
  52         142  
  52         1184  
21 52     52   23342 use Eval::TypeTiny::CodeAccumulator;
  52         250  
  52         1850  
22 52     52   373 use Types::Standard qw( -is -types -assert );
  52         111  
  52         734  
23 52     52   8109 use Types::TypeTiny qw( -is -types to_TypeTiny );
  52         144  
  52         440  
24 52     52   124066 use Type::Params::Parameter;
  52         159  
  52         27782  
25              
26             sub _croak {
27 13     13   83 require Error::TypeTiny;
28 13         60 return Error::TypeTiny::croak( pop );
29             }
30              
31             sub _new_parameter {
32 715     715   1145 shift;
33 715         2344 'Type::Params::Parameter'->new( @_ );
34             }
35              
36             sub _new_code_accumulator {
37 314     314   580 shift;
38 314         1945 'Eval::TypeTiny::CodeAccumulator'->new( @_ );
39             }
40              
41             sub new {
42 290     290 0 552 my $class = shift;
43 290 50       1563 my %self = @_ == 1 ? %{$_[0]} : @_;
  0         0  
44 290         694 my $self = bless \%self, $class;
45 290   50     1139 $self->{parameters} ||= [];
46 290   50     1605 $self->{class_prefix} ||= 'Type::Params::OO::Klass';
47 290         957 $self->BUILD;
48 283         873 return $self;
49             }
50              
51             {
52             my $klass_id;
53             my %klass_cache;
54             sub BUILD {
55 290     290 0 543 my $self = shift;
56              
57 290 100 100     1004 if ( $self->{named_to_list} and not ref $self->{named_to_list} ) {
58 9         17 $self->{named_to_list} = [ map $_->name, @{ $self->{parameters} } ];
  9         38  
59             }
60              
61 290 50       864 if ( delete $self->{rationalize_slurpies} ) {
62 290         921 $self->_rationalize_slurpies;
63             }
64              
65 285 100       962 if ( $self->{method} ) {
66 29         64 my $type = $self->{method};
67             $type =
68             is_Int($type) ? Defined :
69 29 0       187 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 29   50     99 unshift @{ $self->{head} ||= [] }, $self->_new_parameter(
  29         218  
72             name => 'invocant',
73             type => $type,
74             );
75             }
76              
77 285 100 100     1510 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     1565 $self->{bless} = ( $klass_cache{$klass_key} ||= sprintf( '%s%d', $self->{class_prefix}, ++$klass_id ) );
80 30 50       111 $self->{oo_trace} = 1 unless exists $self->{oo_trace};
81 30         259 $self->make_class;
82             }
83 283 100       7114 if ( is_ArrayRef $self->{class} ) {
84 8         30 $self->{constructor} = $self->{class}->[1];
85 8         29 $self->{class} = $self->{class}->[0];
86             }
87             }
88             }
89              
90             sub _klass_key {
91 30     30   65 my $self = shift;
92              
93 30         58 my @parameters = @{ $self->parameters };
  30         74  
94 30 100       103 if ( $self->has_slurpy ) {
95 1         3 push @parameters, $self->slurpy;
96             }
97              
98 52     52   541 no warnings 'uninitialized';
  52         129  
  52         271595  
99             join(
100             '|',
101             map sprintf( '%s*%s*%s', $_->name, $_->getter, $_->predicate ),
102 30         185 sort { $a->{name} cmp $b->{name} } @parameters
  44         188  
103             );
104             }
105              
106             sub _rationalize_slurpies {
107 290     290   556 my $self = shift;
108              
109 290         882 my $parameters = $self->parameters;
110              
111 290 100       817 if ( $self->is_named ) {
    100          
112 155         359 my ( @slurpy, @rest );
113              
114 155         388 for my $parameter ( @$parameters ) {
115 381 100       1139 if ( $parameter->type->is_strictly_a_type_of( Slurpy ) ) {
    100          
116 26         92 push @slurpy, $parameter;
117             }
118             elsif ( $parameter->{slurpy} ) {
119 1         4 $parameter->{type} = Slurpy[ $parameter->type ];
120 1         8 push @slurpy, $parameter;
121             }
122             else {
123 354         998 push @rest, $parameter;
124             }
125             }
126              
127 155 100       688 if ( @slurpy == 1 ) {
    100          
128 25         93 my $constraint = $slurpy[0]->type;
129 25 100 66     114 if ( $constraint->type_parameter && $constraint->type_parameter->{uniq} == Any->{uniq} or $constraint->my_slurp_into eq 'HASH' ) {
      100        
130 24         86 $self->{slurpy} = $slurpy[0];
131 24         99 @$parameters = @rest;
132             }
133             else {
134 1         7 $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 133 100       494 if ( $parameters->[-1]->type->is_strictly_a_type_of( Slurpy ) ) {
    100          
143 40         138 $self->{slurpy} = pop @$parameters;
144             }
145             elsif ( $parameters->[-1]{slurpy} ) {
146 6         16 $self->{slurpy} = pop @$parameters;
147 6         25 $self->{slurpy}{type} = Slurpy[ $self->{slurpy}{type} ];
148             }
149              
150 133         418 for my $parameter ( @$parameters ) {
151 184 100 66     608 if ( $parameter->type->is_strictly_a_type_of( Slurpy ) or $parameter->{slurpy} ) {
152 3         13 $self->_croak( 'Parameter following slurpy parameter' );
153             }
154             }
155             }
156              
157 285 100 100     1444 if ( $self->{slurpy} and $self->{slurpy}->has_default ) {
158 1         7 require Carp;
159 1         3 our @CARP_NOT = ( __PACKAGE__, 'Type::Params' );
160 1         217 Carp::carp( "Warning: the default for the slurpy parameter will be ignored, continuing anyway" );
161 1         10 delete $self->{slurpy}{default};
162             }
163             }
164              
165             sub _parameters_from_list {
166 329     329   1345 my ( $class, $style, $list, %opts ) = @_;
167 329         604 my @return;
168 329         648 my $is_named = ( $style eq 'named' );
169              
170 329         883 while ( @$list ) {
171 686         1119 my ( $type, %param_opts );
172 686 100       1421 if ( $is_named ) {
173 381         1158 $param_opts{name} = assert_Str( shift( @$list ) );
174             }
175 686 100 66     4268 if ( is_HashRef $list->[0] and exists $list->[0]{slurpy} and not is_Bool $list->[0]{slurpy} ) {
      33        
176 2         6 my %new_opts = %{ shift( @$list ) };
  2         10  
177 2         7 $type = delete $new_opts{slurpy};
178 2         9 %param_opts = ( %param_opts, %new_opts, slurpy => 1 );
179             }
180             else {
181 684         1222 $type = shift( @$list );
182             }
183 686 100       2229 if ( is_HashRef( $list->[0] ) ) {
184 60 100 100     241 unless ( exists $list->[0]{slurpy} and not is_Bool $list->[0]{slurpy} ) {
185 58         122 %param_opts = ( %param_opts, %{ +shift( @$list ) } );
  58         220  
186             }
187             }
188             $param_opts{type} =
189 4         27 is_Int($type) ? ( $type ? Any : do { $param_opts{optional} = !!1; Any; } ) :
  4         16  
190 686 100       3338 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 686         2388 my $parameter = $class->_new_parameter( %param_opts );
193 686         2395 push @return, $parameter;
194             }
195              
196 329         1213 return \@return;
197             }
198              
199             sub new_from_compile {
200 290     290 0 703 my $class = shift;
201 290         499 my $style = shift;
202 290         655 my $is_named = ( $style eq 'named' );
203              
204 290         591 my %opts = ();
205 290   66     1868 while ( is_HashRef $_[0] and not exists $_[0]{slurpy} ) {
206 382         879 %opts = ( %opts, %{ +shift } );
  382         2484  
207             }
208              
209 290         753 for my $pos ( qw/ head tail / ) {
210 580 100       1606 next unless defined $opts{$pos};
211 39 100       124 if ( is_Int( $opts{$pos} ) ) {
212 6         22 $opts{$pos} = [ ( Any ) x $opts{$pos} ];
213             }
214 39         152 $opts{$pos} = $class->_parameters_from_list( positional => $opts{$pos}, %opts );
215             }
216              
217 290         781 my $list = [ @_ ];
218 290         686 $opts{is_named} = $is_named;
219 290         1231 $opts{parameters} = $class->_parameters_from_list( $style => $list, %opts );
220              
221 290         1219 my $self = $class->new( %opts, rationalize_slurpies => 1 );
222 283         2218 return $self;
223             }
224              
225             sub new_from_v2api {
226 312     312 0 1190 my ( $class, $opts ) = @_;
227              
228 312   100     1466 my $positional = delete( $opts->{positional} ) || delete( $opts->{pos} );
229 312         664 my $named = delete( $opts->{named} );
230 312   100     1313 my $multiple = delete( $opts->{multiple} ) || delete( $opts->{multi} );
231              
232 312 100 100     1603 $class->_croak( "Signature must be positional, named, or multiple" )
      100        
233             unless $positional || $named || $multiple;
234              
235 310 100       883 if ( $multiple ) {
236 19 100       87 $multiple = [] unless is_ArrayRef $multiple;
237 19 100       48 unshift @$multiple, { positional => $positional } if $positional;
238 19 100       49 unshift @$multiple, { named => $named } if $named;
239 19         2251 require Type::Params::Alternatives;
240 19         141 return 'Type::Params::Alternatives'->new(
241             base_options => $opts,
242             alternatives => $multiple,
243             sig_class => $class,
244             );
245             }
246              
247 291         1112 my ( $sig_kind, $args ) = ( pos => $positional );
248 291 100       767 if ( $named ) {
249 156 100       482 $opts->{bless} = 1 unless exists $opts->{bless};
250 156         361 ( $sig_kind, $args ) = ( named => $named );
251 156 100       392 $class->_croak( "Signature cannot have both positional and named arguments" )
252             if $positional;
253             }
254              
255 289         1063 return $class->new_from_compile( $sig_kind, $opts, @$args );
256             }
257              
258 307     307 0 1191 sub package { $_[0]{package} }
259 307     307 0 2220 sub subname { $_[0]{subname} }
260 301     301 0 1300 sub description { $_[0]{description} } sub has_description { exists $_[0]{description} }
  0     0 0 0  
261 301     301 0 1108 sub method { $_[0]{method} }
262 1379     1379 0 4033 sub head { $_[0]{head} } sub has_head { exists $_[0]{head} }
  339     339 0 1799  
263 1195     1195 0 3079 sub tail { $_[0]{tail} } sub has_tail { exists $_[0]{tail} }
  64     64 0 124  
264 1     1 0 264 sub parameters { $_[0]{parameters} } sub has_parameters { exists $_[0]{parameters} }
  1054     1054 0 3130  
265 707     707 0 2291 sub slurpy { $_[0]{slurpy} } sub has_slurpy { exists $_[0]{slurpy} }
  221     221 0 754  
266 1708     1708 0 11253 sub on_die { $_[0]{on_die} } sub has_on_die { exists $_[0]{on_die} }
  6     6 0 23  
267 1102     1102 0 3208 sub strictness { $_[0]{strictness} } sub has_strictness { exists $_[0]{strictness} }
  706     706 0 3284  
268 581     581 0 2892 sub goto_next { $_[0]{goto_next} }
269 1936     1936 0 6355 sub is_named { $_[0]{is_named} }
270 509     509 0 1950 sub bless { $_[0]{bless} }
271 163     163 0 563 sub class { $_[0]{class} }
272 24     24 0 139 sub constructor { $_[0]{constructor} }
273 180     180 0 844 sub named_to_list { $_[0]{named_to_list} }
274 44     44 0 180 sub oo_trace { $_[0]{oo_trace} }
275              
276 42 100   42 0 267 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 448 100   448 0 1522 if exists $_[0]{can_shortcut};
281             $_[0]{can_shortcut} = !(
282             $_[0]->slurpy or
283 150   100     425 grep $_->might_supply_new_value, @{ $_[0]->parameters }
284             );
285             }
286              
287             sub coderef {
288 301   66 301 0 1574 $_[0]{coderef} ||= $_[0]->_build_coderef;
289             }
290              
291             sub _build_coderef {
292 301     301   560 my $self = shift;
293 301   66     924 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 301         1406 $self->_coderef_start( $coderef );
299 299 100       654 $self->_coderef_head( $coderef ) if $self->has_head;
300 299 100       766 $self->_coderef_tail( $coderef ) if $self->has_tail;
301 299         1102 $self->_coderef_parameters( $coderef );
302 298 100       810 if ( $self->has_slurpy ) {
    100          
303 69         244 $self->_coderef_slurpy( $coderef );
304             }
305             elsif ( $self->is_named ) {
306 126         401 $self->_coderef_extra_names( $coderef );
307             }
308 298         1103 $self->_coderef_end( $coderef );
309              
310 298         1484 return $coderef;
311             }
312              
313             sub _coderef_start {
314 301     301   735 my ( $self, $coderef ) = ( shift, @_ );
315              
316 301         1878 $coderef->add_line( 'sub {' );
317 301         754 $coderef->{indent} .= "\t";
318              
319 301 100       840 if ( my $next = $self->goto_next ) {
320 33 100       183 if ( is_CodeLike $next ) {
321 32         144 $coderef->add_variable( '$__NEXT__', \$next );
322             }
323             else {
324 1         5 $coderef->add_line( 'my $__NEXT__ = shift;' );
325 1         5 $coderef->add_gap;
326             }
327             }
328              
329 301 100       954 if ( $self->method ) {
330             # Passed to parameter defaults
331 38         87 $self->{method_invocant} = '$__INVOCANT__';
332 38         112 $coderef->add_line( sprintf 'my %s = $_[0];', $self->method_invocant );
333 38         110 $coderef->add_gap;
334             }
335              
336 301         982 $self->_coderef_start_extra( $coderef );
337              
338 300         513 my $extravars = '';
339 300 100       845 if ( $self->has_head ) {
340 51         106 $extravars .= ', @head';
341             }
342 300 100       852 if ( $self->has_tail ) {
343 16         32 $extravars .= ', @tail';
344             }
345              
346 300 100       770 if ( $self->is_named ) {
    100          
347 150         596 $coderef->add_line( "my ( \%out, \%in, \%tmp, \$tmp, \$dtmp$extravars );" );
348             }
349             elsif ( $self->can_shortcut ) {
350 85         413 $coderef->add_line( "my ( \%tmp, \$tmp$extravars );" );
351             }
352             else {
353 65         283 $coderef->add_line( "my ( \@out, \%tmp, \$tmp, \$dtmp$extravars );" );
354             }
355              
356 300 100       971 if ( $self->has_on_die ) {
357 6         20 $coderef->add_variable( '$__ON_DIE__', \ $self->on_die );
358             }
359              
360 300         1102 $coderef->add_gap;
361              
362 300         997 $self->_coderef_check_count( $coderef );
363              
364 299         973 $coderef->add_gap;
365              
366 299         596 $self;
367             }
368              
369       282     sub _coderef_start_extra {}
370              
371             sub _coderef_check_count {
372 282     282   677 my ( $self, $coderef ) = ( shift, @_ );
373              
374 282         499 my $strictness_test = '';
375 282 100 100     669 if ( defined $self->strictness and $self->strictness eq 1 ) {
    100          
    100          
376 1         2 $strictness_test = '';
377             }
378             elsif ( $self->strictness ) {
379 3         11 $strictness_test = sprintf '( not %s ) or ', $self->strictness;
380             }
381             elsif ( $self->has_strictness ) {
382 1         4 return $self;
383             }
384              
385 281         625 my $headtail = 0;
386 281 100       629 $headtail += @{ $self->head } if $self->has_head;
  51         121  
387 281 100       716 $headtail += @{ $self->tail } if $self->has_tail;
  16         34  
388              
389 281         699 my $is_named = $self->is_named;
390 281         489 my $min_args = 0;
391 281         492 my $max_args = 0;
392 281         529 my $seen_optional = 0;
393 281         477 for my $parameter ( @{ $self->parameters } ) {
  281         725  
394 529 100       1550 if ( $parameter->optional ) {
395 117         1796 ++$seen_optional;
396 117         254 ++$max_args;
397             }
398             else {
399 412 100 100     17637 $seen_optional and !$is_named and $self->_croak(
400             'Non-Optional parameter following Optional parameter',
401             );
402 411         655 ++$max_args;
403 411         750 ++$min_args;
404             }
405             }
406              
407 280 100       840 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 280 100       703 if ( $is_named ) {
417 150         302 my $args_if_hashref = $headtail + 1;
418 150 100       259 my $hashref_index = @{ $self->head || [] };
  150         440  
419 150         370 my $arity_if_hash = $headtail % 2;
420 150         355 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 150         889 require List::Util;
426 150         874 $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 150         324 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 150 100       535 $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 150         620 $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 130         235 $min_args += $headtail;
456 130 100       369 $max_args += $headtail if defined $max_args;
457              
458 130         307 $self->{min_args} = $min_args;
459 130         298 $self->{max_args} = $max_args;
460              
461 130 100 100     716 if ( defined $max_args and $min_args == $max_args ) {
    100 100        
462 67         313 $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 10         40 $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     385 $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 51     51   121 my ( $self, $coderef ) = ( shift, @_ );
502 51 50       105 $self->has_head or return;
503              
504 51         93 my $size = @{ $self->head };
  51         111  
505 51         223 $coderef->add_line( sprintf(
506             '@head = splice( @_, 0, %d );',
507             $size,
508             ) );
509              
510 51         152 $coderef->add_gap;
511              
512 51         96 my $i = 0;
513 51         74 for my $parameter ( @{ $self->head } ) {
  51         117  
514 59         436 $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 59         154 ++$i;
526             }
527              
528 51         103 $self;
529             }
530              
531             sub _coderef_tail {
532 16     16   37 my ( $self, $coderef ) = ( shift, @_ );
533 16 50       43 $self->has_tail or return;
534              
535 16         29 my $size = @{ $self->tail };
  16         34  
536 16         74 $coderef->add_line( sprintf(
537             '@tail = splice( @_, -%d );',
538             $size,
539             ) );
540              
541 16         44 $coderef->add_gap;
542              
543 16         27 my $i = 0;
544 16         25 my $n = @{ $self->tail };
  16         34  
545 16         26 for my $parameter ( @{ $self->tail } ) {
  16         35  
546 42         247 $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         99 ++$i;
558             }
559              
560 16         34 $self;
561             }
562              
563             sub _coderef_parameters {
564 299     299   742 my ( $self, $coderef ) = ( shift, @_ );
565              
566 299 100       697 if ( $self->is_named ) {
567              
568 150         507 $coderef->add_line( sprintf(
569             '%%in = ( @_ == 1 and %s ) ? %%{ $_[0] } : @_;',
570             HashRef->inline_check( '$_[0]' ),
571             ) );
572              
573 150         523 $coderef->add_gap;
574              
575 150         271 for my $parameter ( @{ $self->parameters } ) {
  150         352  
576 350         1059 my $qname = B::perlstring( $parameter->name );
577 350         1547 $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 149         383 my $can_shortcut = $self->can_shortcut;
591 149 100       376 my $head_size = $self->has_head ? @{ $self->head } : 0;
  32         77  
592              
593 149         297 my $i = 0;
594 149         259 for my $parameter ( @{ $self->parameters } ) {
  149         346  
595 179 100       1437 $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 178         484 ++$i;
607             }
608             }
609             }
610              
611             sub _coderef_slurpy {
612 69     69   215 my ( $self, $coderef ) = ( shift, @_ );
613 69 50       210 return unless $self->has_slurpy;
614              
615 69         204 my $parameter = $self->slurpy;
616 69         236 my $constraint = $parameter->type;
617 69         575 my $slurp_into = $constraint->my_slurp_into;
618 69         479 my $real_type = $constraint->my_unslurpy;
619              
620 69 100 66     250 if ( $self->is_named ) {
    100          
    100          
621 24         98 $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         3  
628             ) );
629             }
630             elsif ( $slurp_into eq 'HASH' ) {
631              
632 29         220 my $index = scalar( @{ $self->parameters } );
  29         77  
633 29   33     96 $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         103 scalar( @{ $self->parameters } ),
  15         41  
654             ) );
655             }
656              
657 69         351 $coderef->add_gap;
658              
659 69 100       247 $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 126     126   330 my ( $self, $coderef ) = ( shift, @_ );
673              
674 126 50 33     316 return $self if $self->has_strictness && ! $self->strictness;
675              
676 126         7695 require Type::Utils;
677 126         292 my $english_list = 'Type::Utils::english_list';
678 126 100       352 if ( $Type::Tiny::AvoidCallbacks ) {
679 8         18 $english_list = 'join q{, } => ';
680             }
681              
682 126         411 $coderef->add_line( '# Unrecognized parameters' );
683 126 50 33     547 $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 126         345 $coderef->add_gap;
694             }
695              
696             sub _coderef_end {
697 298     298   684 my ( $self, $coderef ) = ( shift, @_ );
698              
699 298 100 100     769 if ( $self->bless and $self->oo_trace ) {
700 28         91 my $package = $self->package;
701 28         84 my $subname = $self->subname;
702 28 50 33     137 if ( defined $package and defined $subname ) {
703 28         244 $coderef->add_line( sprintf(
704             '$out{"~~caller"} = %s;',
705             B::perlstring( "$package\::$subname" ),
706             ) );
707 28         76 $coderef->add_gap;
708             }
709             }
710              
711 298         1002 $self->_coderef_end_extra( $coderef );
712 298         865 $coderef->add_line( $self->_make_return_expression( is_early => 0 ) . ';' );
713 298         1545 $coderef->{indent} =~ s/\t$//;
714 298         1021 $coderef->add_line( '}' );
715              
716 298         524 $self;
717             }
718              
719       280     sub _coderef_end_extra {}
720              
721             sub _make_return_list {
722 299     299   554 my $self = shift;
723              
724 299         519 my @return_list;
725 299 100       707 if ( $self->has_head ) {
726 51         116 push @return_list, '@head';
727             }
728              
729 299 100       900 if ( not $self->is_named ) {
    100          
    100          
    100          
730 149 100       384 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 11         22 @{ $self->named_to_list },
  11         31  
736             );
737             }
738             elsif ( $self->class ) {
739 24   100     80 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         104 push @return_list, sprintf(
747             'bless( \%%out, %s )',
748             B::perlstring( $self->bless ),
749             );
750             }
751             else {
752 79         178 push @return_list, '\%out';
753             }
754              
755 299 100       855 if ( $self->has_tail ) {
756 16         31 push @return_list, '@tail';
757             }
758              
759 299         984 return @return_list;
760             }
761              
762             sub _make_return_expression {
763 317     317   920 my ( $self, %args ) = @_;
764              
765 317         814 my $list = join q{, }, $self->_make_return_list;
766              
767 317 100 66     790 if ( $self->goto_next ) {
    100          
768 33 100       148 if ( $list eq '@_' ) {
769 7         44 return sprintf 'goto( $__NEXT__ )';
770             }
771             else {
772 26         181 return sprintf 'do { @_ = ( %s ); goto $__NEXT__ }',
773             $list;
774             }
775             }
776             elsif ( $args{is_early} or not exists $args{is_early} ) {
777 19         100 return sprintf 'return( %s )', $list;
778             }
779             else {
780 265         1688 return sprintf '( %s )', $list;
781             }
782             }
783              
784             sub _make_general_fail {
785 445     445   1465 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 445 100       1128 );
793             }
794              
795             sub _make_constraint_fail {
796 683     683   3435 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 683 100 33     1720 B::perlstring( $args{display_var} || $args{varname} ),
806             );
807             }
808              
809             sub _make_count_fail {
810 280     280   1251 my ( $self, %args ) = ( shift, @_ );
811              
812 280         533 my @counts;
813 280 50       886 if ( $args{got} ) {
814             push @counts, sprintf(
815             'got => %s',
816             $args{got},
817 280         1093 );
818             }
819 280         664 for my $c ( qw/ minimum maximum / ) {
820 560 100       2447 is_Int( $args{$c} ) or next;
821             push @counts, sprintf(
822             '%s => %s',
823             $c,
824 207         744 $args{$c},
825             );
826             }
827              
828 280 100       824 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 78 my $self = shift;
838 43   66     194 $self->{class_attributes} ||= $self->_build_class_attributes;
839             }
840              
841             sub _build_class_attributes {
842 30     30   57 my $self = shift;
843 30         67 my %predicates;
844             my %getters;
845              
846 30         63 my @parameters = @{ $self->parameters };
  30         78  
847 30 100       85 if ( $self->has_slurpy ) {
848 1         4 push @parameters, $self->slurpy;
849             }
850              
851 30         87 for my $parameter ( @parameters ) {
852              
853 63         178 my $name = $parameter->name;
854 63 100       158 if ( my $predicate = $parameter->predicate ) {
855 20 50       87 $predicate =~ /^[^0-9\W]\w*$/
856             or $self->_croak( "Bad accessor name: \"$predicate\"" );
857 20         60 $predicates{$predicate} = $name;
858             }
859 63 50       170 if ( my $getter = $parameter->getter ) {
860 63 100       311 $getter =~ /^[^0-9\W]\w*$/
861             or $self->_croak( "Bad accessor name: \"$getter\"" );
862 61         185 $getters{$getter} = $name;
863             }
864             }
865              
866             return {
867 28         190 exists_predicates => \%predicates,
868             getters => \%getters,
869             };
870             }
871              
872             sub make_class {
873 30     30 0 65 my $self = shift;
874            
875 30   50     211 my $env = uc( $ENV{PERL_TYPE_PARAMS_XS} || 'XS' );
876 30 50 33     169 if ( $env eq 'PP' or $ENV{PERL_ONLY} ) {
877 0         0 $self->make_class_pp;
878             }
879              
880 30         101 $self->make_class_xs;
881             }
882              
883             sub make_class_xs {
884 30     30 0 56 my $self = shift;
885              
886 30 50       82 eval {
887 30         4782 require Class::XSAccessor;
888 30         19888 'Class::XSAccessor'->VERSION( '1.17' );
889 30         167 1;
890             } or return $self->make_class_pp;
891              
892 30         109 my $attr = $self->class_attributes;
893              
894 28         103 '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 48     48 0 102 my $self = shift;
913              
914 48 100 66     100 return ''
      100        
915             unless $self->is_named && $self->bless && !$self->named_to_list;
916              
917 13         51 my $coderef = $self->_new_code_accumulator;
918 13         44 my $attr = $self->class_attributes;
919              
920 13         48 $coderef->add_line( '{' );
921 13         34 $coderef->{indent} = "\t";
922 13         37 $coderef->add_line( sprintf( 'package %s;', $self->bless ) );
923 13         93 $coderef->add_line( 'use strict;' );
924 13         42 $coderef->add_line( 'no warnings;' );
925              
926 13         28 for my $function ( sort keys %{ $attr->{getters} } ) {
  13         68  
927 28         53 my $slot = $attr->{getters}{$function};
928 28         137 $coderef->add_line( sprintf(
929             'sub %s { $_[0]{%s} }',
930             $function,
931             B::perlstring( $slot ),
932             ) );
933             }
934              
935 13         28 for my $function ( sort keys %{ $attr->{exists_predicates} } ) {
  13         45  
936 12         21 my $slot = $attr->{exists_predicates}{$function};
937 12         54 $coderef->add_line( sprintf(
938             'sub %s { exists $_[0]{%s} }',
939             $function,
940             B::perlstring( $slot ),
941             ) );
942             }
943            
944 13         39 $coderef->add_line( '1;' );
945 13         32 $coderef->{indent} = "";
946 13         52 $coderef->add_line( '}' );
947              
948 13         36 return $coderef->code;
949             }
950              
951             sub return_wanted {
952 291     291 0 585 my $self = shift;
953 291         897 my $coderef = $self->coderef;
954              
955 288 100       1446 if ( $self->{want_source} ) {
    100          
    100          
956 7         34 return $coderef->code;
957             }
958             elsif ( $self->{want_object} ) { # undocumented for now
959 1         7 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 52         225 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         825 return $coderef->compile;
974             }
975              
976             1;