File Coverage

blib/lib/Eval/TypeTiny.pm
Criterion Covered Total %
statement 145 153 94.7
branch 79 98 80.6
condition 38 61 62.3
subroutine 25 25 100.0
pod 4 4 100.0
total 291 341 85.3


line stmt bran cond sub pod time code
1             package Eval::TypeTiny;
2              
3 275     275   76275 use strict;
  275         619  
  275         21663  
4              
5             sub _clean_eval {
6 28770     28770   39892 local $@;
7 28770         107338 local $SIG{__DIE__};
8 28770     13   5950225 my $r = eval $_[0];
  3     7   18  
  3     4   9  
  3     6   422  
  1     1   6  
  1     1   1  
  2     1   40  
  2     1   16  
  2     1   4  
  2         153  
  6         19  
  6         65  
  1         45  
  1         6  
  1         12  
  1         119  
  1         6  
  1         2  
  1         31  
  1         6  
  1         1  
  1         113  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
9 28770         2263457 my $e = $@;
10 28770         152931 return ( $r, $e );
11             }
12              
13 275     275   1880 use warnings;
  275         591  
  275         34254  
14              
15             BEGIN {
16 275 50   275   2199 *HAS_LEXICAL_SUBS = ( $] >= 5.018 ) ? sub () { !!1 } : sub () { !!0 };
17 275 50       93319 *NICE_PROTOTYPES = ( $] >= 5.014 ) ? sub () { !!1 } : sub () { !!0 };
18             }
19              
20             sub _pick_alternative {
21 259     259   3681 my $ok = 0;
22 259         1032 while ( @_ ) {
23 259         1281 my ( $type, $condition, $result ) = splice @_, 0, 3;
24 259 100       961 if ( $type eq 'needs' ) {
    50          
25 256 50       17648 ++$ok if eval "require $condition; 1";
26             }
27             elsif ( $type eq 'if' ) {
28 3 100       9 ++$ok if $condition;
29             }
30 259 100       1313 next unless $ok;
31 258 100       12889 return ref( $result ) eq 'CODE' ? $result->() : ref( $result ) eq 'SCALAR' ? eval( $$result ) : $result;
    50          
32             }
33 1         3 return;
34             }
35              
36             {
37             sub IMPLEMENTATION_DEVEL_LEXALIAS () { 'Devel::LexAlias' }
38             sub IMPLEMENTATION_PADWALKER () { 'PadWalker' }
39             sub IMPLEMENTATION_TIE () { 'tie' }
40             sub IMPLEMENTATION_NATIVE () { 'perl' }
41            
42             my $implementation;
43            
44             #<<<
45             # uncoverable subroutine
46             sub ALIAS_IMPLEMENTATION () {
47 104   66 104 1 423 $implementation ||= _pick_alternative(
48             if => ( $] ge '5.022' ) => IMPLEMENTATION_NATIVE,
49             needs => 'Devel::LexAlias' => IMPLEMENTATION_DEVEL_LEXALIAS,
50             needs => 'PadWalker' => IMPLEMENTATION_PADWALKER,
51             if => !!1 => IMPLEMENTATION_TIE,
52             );
53             }
54             #>>>
55            
56             sub _force_implementation {
57 5     5   3384 $implementation = shift;
58             }
59             }
60              
61             BEGIN {
62 275 100   275   31993 *_EXTENDED_TESTING = $ENV{EXTENDED_TESTING} ? sub() { !!1 } : sub() { !!0 };
63             }
64              
65             our $AUTHORITY = 'cpan:TOBYINK';
66             our $VERSION = '2.004000';
67             our @EXPORT = qw( eval_closure );
68             our @EXPORT_OK = qw(
69             HAS_LEXICAL_SUBS HAS_LEXICAL_VARS ALIAS_IMPLEMENTATION
70             IMPLEMENTATION_DEVEL_LEXALIAS IMPLEMENTATION_PADWALKER
71             IMPLEMENTATION_NATIVE IMPLEMENTATION_TIE
72             set_subname type_to_coderef NICE_PROTOTYPES
73             );
74              
75             $VERSION =~ tr/_//d;
76              
77             # See Types::TypeTiny for an explanation of this import method.
78             #
79             # uncoverable subroutine
80             sub import {
81 275     275   2128 no warnings "redefine";
  275         701  
  275         507765  
82 7     17   77 our @ISA = qw( Exporter::Tiny );
83 7         23 require Exporter::Tiny;
84 7         269 my $next = \&Exporter::Tiny::import;
85 10         1746 *import = $next;
86 13         89 my $class = shift;
87 13 50       2388 my $opts = { ref( $_[0] ) ? %{ +shift } : () };
  7         47  
88 5   33     23 $opts->{into} ||= scalar( caller );
89 5         357 return $class->$next( $opts, @_ );
90             } #/ sub import
91              
92             {
93             my $subname;
94             my %already; # prevent renaming established functions
95             sub set_subname ($$) {
96 63636 100   63636 1 123356 $subname = _pick_alternative(
97             needs => 'Sub::Util' => \ q{ \&Sub::Util::set_subname },
98             needs => 'Sub::Name' => \ q{ \&Sub::Name::subname },
99             if => !!1 => 0,
100             ) unless defined $subname;
101 63636 100 66     577715 $subname and !$already{$_[1]}++ and return &$subname;
102 18940         37260 $_[1];
103             } #/ sub set_subname ($$)
104             }
105              
106             sub type_to_coderef {
107 13866     13866 1 28373 my ( $type, %args ) = @_;
108 13866   50     42067 my $post_method = $args{post_method} || q();
109            
110 13866         21022 my ( $coderef, $qualified_name );
111            
112 13866 100       26431 if ( ! defined $type ) {
113 809         1254 my $library = $args{type_library};
114 809         1159 my $name = $args{type_name};
115            
116 809         1604 $qualified_name = "$library\::$name";
117             $coderef = sub (;@) {
118 424     424   2550 my $params;
119 424 50       1275 $params = shift if ref( $_[0] ) eq "ARRAY";
120            
121 424   100     1352 $type ||= do {
122 422 50 0     3171 $library->can( 'get_type' )
123             or require Error::TypeTiny
124             && Error::TypeTiny::croak( "Expected $library to be a type library, but it doesn't seem to be" );
125 422         1573 $library->get_type( $name );
126             };
127            
128 424         761 my $t;
129 424 100       1018 if ( $type ) {
130 3 50       11 $t = $params ? $type->parameterize( @$params ) : $type;
131 3 50       10 $t = $t->$post_method if $post_method;
132             }
133             else {
134 421 50 0     981 require Error::TypeTiny && Error::TypeTiny::croak( "Cannot parameterize a non-existant type" )
135             if $params;
136 421         20671 require Type::Tiny::_DeclaredType;
137 421         2373 $t = Type::Tiny::_DeclaredType->new( library => $library, name => $name );
138             }
139            
140 424 100 66     2986 @_ && wantarray ? return ( $t, @_ ) : return $t;
141 809         5695 };
142            
143 809 50       6443 require Scalar::Util && &Scalar::Util::set_prototype( $coderef, ';$' )
144             if Eval::TypeTiny::NICE_PROTOTYPES;
145             }
146             else {
147            
148             #<<<
149 13057 100       31622 my $source = $type->is_parameterizable ?
150             sprintf(
151             q{
152             sub (%s) {
153             if (ref($_[0]) eq 'Type::Tiny::_HalfOp') {
154             my $complete_type = shift->complete($type);
155             @_ && wantarray ? return($complete_type, @_) : return $complete_type;
156             }
157             my $params; $params = shift if ref($_[0]) eq q(ARRAY);
158             my $t = $params ? $type->parameterize(@$params) : $type;
159             @_ && wantarray ? return($t%s, @_) : return $t%s;
160             }
161             },
162             NICE_PROTOTYPES ? q(;$) : q(;@),
163             $post_method,
164             $post_method,
165             ) :
166             sprintf( q{ sub () { $type%s if $] } }, $post_method );
167             #>>>
168            
169 13057         34809 $qualified_name = $type->qualified_name;
170             $coderef = eval_closure(
171             source => $source,
172 13057   33     78811 description => $args{description} || sprintf( "exportable function '%s'", $qualified_name ),
173             environment => { '$type' => \$type },
174             );
175             }
176            
177 13866 50       54753 $args{anonymous} ? $coderef : set_subname( $qualified_name, $coderef );
178             }
179              
180             sub eval_closure {
181 28771     28771 1 116460 my ( %args ) = @_;
182             my $src =
183             ref $args{source} eq "ARRAY"
184 2         18 ? join( "\n", @{ $args{source} } )
185 28771 100       76898 : $args{source};
186            
187 28771 100       74787 $args{alias} = 0 unless defined $args{alias};
188 28771 100       65056 $args{line} = 1 unless defined $args{line};
189             $args{description} =~ s/[^\w .:-\[\]\(\)\{\}\']//g
190 28771 100       100940 if defined $args{description};
191             $src = qq{#line $args{line} "$args{description}"\n$src}
192 28771 100 66     178583 if defined $args{description} && !( $^P & 0x10 );
193 28771   100     96290 $args{environment} ||= {};
194            
195 28771         39301 if ( _EXTENDED_TESTING ) {
196 28771         123005 require Scalar::Util;
197 28771         43064 for my $k ( sort keys %{ $args{environment} } ) {
  28771         110833  
198             next
199             if $k =~ /^\$/
200 13795 100 66     121695 && Scalar::Util::reftype( $args{environment}{$k} ) =~ /^(SCALAR|REF)$/;
201             next
202             if $k =~ /^\@/
203 521 100 66     2242 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(ARRAY);
204             next
205             if $k =~ /^\%/
206 274 100 100     913 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(HASH);
207             next
208             if $k =~ /^\&/
209 267 100 66     649 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(CODE);
210            
211 1         4 require Error::TypeTiny;
212             Error::TypeTiny::croak(
213             "Expected a variable name and ref; got %s => %s", $k,
214 265         583 $args{environment}{$k}
215             );
216             } #/ for my $k ( sort keys %...)
217             } #/ if ( _EXTENDED_TESTING)
218            
219 28770         54991 my $sandpkg = 'Eval::TypeTiny::Sandbox';
220 28506 100       58212 my $alias = exists( $args{alias} ) ? $args{alias} : 0;
221 28506         36800 my @keys = sort keys %{ $args{environment} };
  28506         66937  
222 28506         43240 my $i = 0;
223 28770         94032 my $source = join "\n" => (
224             "package $sandpkg;",
225             "sub {",
226             map( _make_lexical_assignment( $_, $i++, $alias ), @keys ),
227             $src,
228             "}",
229             );
230            
231 28770 100 100     71359 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_TIE ) {
232 269         1649 _manufacture_ties();
233             }
234            
235 28770         56841 my ( $compiler, $e ) = _clean_eval( $source );
236 28506 100       80903 if ( $e ) {
237 4         17 chomp $e;
238 268         1787 require Error::TypeTiny::Compilation;
239             "Error::TypeTiny::Compilation"->throw(
240             code => (
241 1         5 ref $args{source} eq "ARRAY" ? join( "\n", @{ $args{source} } ) : $args{source}
242             ),
243             errstr => $e,
244             environment => $args{environment},
245 4 100       55 );
246             } #/ if ( $e )
247            
248 28502         49537 my $code = $compiler->( @{ $args{environment} }{@keys} );
  28766         374238  
249 28502         156840 undef( $compiler );
250            
251 28502 100 100     69983 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
252 3         14 require Devel::LexAlias;
253             Devel::LexAlias::lexalias( $code, $_ => $args{environment}{$_} )
254 3         20 for grep !/^\&/, @keys;
255             }
256            
257 28502 100 100     57318 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
258 3         13 require PadWalker;
259 3         20 my %env = map +( $_ => $args{environment}{$_} ), grep !/^\&/, @keys;
260 3         13 PadWalker::set_closed_over( $code, \%env );
261             }
262            
263 28502         155962 return $code;
264             } #/ sub eval_closure
265              
266             my $tmp;
267              
268             sub _make_lexical_assignment {
269 13650     13650   28583 my ( $key, $index, $alias ) = @_;
270 13650         28730 my $name = substr( $key, 1 );
271            
272 13650 100       31033 if ( HAS_LEXICAL_SUBS and $key =~ /^\&/ ) {
273 2         9 $tmp++;
274 2         5 my $tmpname = '$__LEXICAL_SUB__' . $tmp;
275             return
276 2         13 "no warnings 'experimental::lexical_subs';"
277             . "use feature 'lexical_subs';"
278             . "my $tmpname = \$_[$index];"
279             . "my sub $name { goto $tmpname };";
280             }
281            
282 13648 100       24962 if ( !$alias ) {
    100          
    100          
    100          
283 13624         23389 my $sigil = substr( $key, 0, 1 );
284 13624         76154 return "my $key = $sigil\{ \$_[$index] };";
285             }
286             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_NATIVE ) {
287             return
288 7         55 "no warnings 'experimental::refaliasing';"
289             . "use feature 'refaliasing';"
290             . "my $key; \\$key = \$_[$index];";
291             }
292             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
293 5         24 return "my $key;";
294             }
295             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
296 5         27 return "my $key;";
297             }
298             else {
299             my $tieclass = {
300             '@' => 'Eval::TypeTiny::_TieArray',
301             '%' => 'Eval::TypeTiny::_TieHash',
302             '$' => 'Eval::TypeTiny::_TieScalar',
303 7         28 }->{ substr( $key, 0, 1 ) };
304            
305 7         62 return sprintf(
306             'tie(my(%s), "%s", $_[%d]);',
307             $key,
308             $tieclass,
309             $index,
310             );
311             } #/ else [ if ( !$alias ) ]
312             } #/ sub _make_lexical_assignment
313              
314             {
315             my $tie;
316            
317             sub _manufacture_ties {
318 6 50 66 6   2519 $tie ||= eval <<'FALLBACK'; } }
    100 66        
    50 0        
    0          
    0          
319             no warnings qw(void once uninitialized numeric);
320             use Type::Tiny ();
321              
322             {
323             package #
324             Eval::TypeTiny::_TieArray;
325             require Tie::Array;
326             our @ISA = qw( Tie::StdArray );
327             sub TIEARRAY {
328             my $class = shift;
329             bless $_[0] => $class;
330             }
331             sub AUTOLOAD {
332             my $self = shift;
333             my ($method) = (our $AUTOLOAD =~ /(\w+)$/);
334             defined tied(@$self) and return tied(@$self)->$method(@_);
335             require Carp;
336             Carp::croak(qq[Can't call method "$method" on an undefined value]) unless $method eq 'DESTROY';
337             }
338             sub can {
339             my $self = shift;
340             my $code = $self->SUPER::can(@_)
341             || (defined tied(@$self) and tied(@$self)->can(@_));
342             return $code;
343             }
344             __PACKAGE__->Type::Tiny::_install_overloads(
345             q[bool] => sub { !! tied @{$_[0]} },
346             q[""] => sub { '' . tied @{$_[0]} },
347             q[0+] => sub { 0 + tied @{$_[0]} },
348             );
349             }
350             {
351             package #
352             Eval::TypeTiny::_TieHash;
353             require Tie::Hash;
354             our @ISA = qw( Tie::StdHash );
355             sub TIEHASH {
356             my $class = shift;
357             bless $_[0] => $class;
358             }
359             sub AUTOLOAD {
360             my $self = shift;
361             my ($method) = (our $AUTOLOAD =~ /(\w+)$/);
362             defined tied(%$self) and return tied(%$self)->$method(@_);
363             require Carp;
364             Carp::croak(qq[Can't call method "$method" on an undefined value]) unless $method eq 'DESTROY';
365             }
366             sub can {
367             my $self = shift;
368             my $code = $self->SUPER::can(@_)
369             || (defined tied(%$self) and tied(%$self)->can(@_));
370             return $code;
371             }
372             __PACKAGE__->Type::Tiny::_install_overloads(
373             q[bool] => sub { !! tied %{$_[0]} },
374             q[""] => sub { '' . tied %{$_[0]} },
375             q[0+] => sub { 0 + tied %{$_[0]} },
376             );
377             }
378             {
379             package #
380             Eval::TypeTiny::_TieScalar;
381             require Tie::Scalar;
382             our @ISA = qw( Tie::StdScalar );
383             sub TIESCALAR {
384             my $class = shift;
385             bless $_[0] => $class;
386             }
387             sub AUTOLOAD {
388             my $self = shift;
389             my ($method) = (our $AUTOLOAD =~ /(\w+)$/);
390             defined tied($$self) and return tied($$self)->$method(@_);
391             require Carp;
392             Carp::croak(qq[Can't call method "$method" on an undefined value]) unless $method eq 'DESTROY';
393             }
394             sub can {
395             my $self = shift;
396             my $code = $self->SUPER::can(@_)
397             || (defined tied($$self) and tied($$self)->can(@_));
398             return $code;
399             }
400             __PACKAGE__->Type::Tiny::_install_overloads(
401             q[bool] => sub { !! tied ${$_[0]} },
402             q[""] => sub { '' . tied ${$_[0]} },
403             q[0+] => sub { 0 + tied ${$_[0]} },
404             );
405             }
406              
407             1;
408             FALLBACK
409              
410             1;
411              
412             __END__