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 268     268   79649 use strict;
  268         564  
  268         21735  
4              
5             sub _clean_eval {
6 27123     27123   37894 local $@;
7 27123         102053 local $SIG{__DIE__};
8 27123     15   5773979 my $r = eval $_[0];
  8     10   2325  
  8     4   34  
  8     6   486  
  3     1   19  
  3     1   208  
  2     1   43  
  2     1   16  
  2     1   4  
  2         178  
  6         18  
  6         58  
  1         61  
  1         7  
  1         1  
  1         117  
  1         6  
  1         2  
  1         70  
  1         7  
  1         2  
  1         114  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
9 27123         2172263 my $e = $@;
10 27123         143090 return ( $r, $e );
11             }
12              
13 268     268   1889 use warnings;
  268         636  
  268         34943  
14              
15             BEGIN {
16 268 50   268   2073 *HAS_LEXICAL_SUBS = ( $] >= 5.018 ) ? sub () { !!1 } : sub () { !!0 };
17 268 50       94467 *NICE_PROTOTYPES = ( $] >= 5.014 ) ? sub () { !!1 } : sub () { !!0 };
18             }
19              
20             sub _pick_alternative {
21 253     253   4158 my $ok = 0;
22 253         1024 while ( @_ ) {
23 253         1275 my ( $type, $condition, $result ) = splice @_, 0, 3;
24 253 100       1029 if ( $type eq 'needs' ) {
    50          
25 250 50       17600 ++$ok if eval "require $condition; 1";
26             }
27             elsif ( $type eq 'if' ) {
28 3 100       8 ++$ok if $condition;
29             }
30 253 100       1413 next unless $ok;
31 252 100       12806 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 377 $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   4069 $implementation = shift;
58             }
59             }
60              
61             BEGIN {
62 268 100   268   33374 *_EXTENDED_TESTING = $ENV{EXTENDED_TESTING} ? sub() { !!1 } : sub() { !!0 };
63             }
64              
65             our $AUTHORITY = 'cpan:TOBYINK';
66             our $VERSION = '2.002001';
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 268     268   1973 no warnings "redefine";
  268         633  
  268         509507  
82 6     13   58 our @ISA = qw( Exporter::Tiny );
83 6         16 require Exporter::Tiny;
84 6         255 my $next = \&Exporter::Tiny::import;
85 6         1927 *import = $next;
86 6         23 my $class = shift;
87 6 50       2360 my $opts = { ref( $_[0] ) ? %{ +shift } : () };
  6         26  
88 7   33     55 $opts->{into} ||= scalar( caller );
89 7         142 return $class->$next( $opts, @_ );
90             } #/ sub import
91              
92             {
93             my $subname;
94             my %already; # prevent renaming established functions
95             sub set_subname ($$) {
96 59671 100   59671 1 115859 $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 59671 100 66     542513 $subname and !$already{$_[1]}++ and return &$subname;
102 17627         34945 $_[1];
103             } #/ sub set_subname ($$)
104             }
105              
106             sub type_to_coderef {
107 12973     12973 1 26361 my ( $type, %args ) = @_;
108 12973   50     38760 my $post_method = $args{post_method} || q();
109            
110 12973         21286 my ( $coderef, $qualified_name );
111            
112 12973 100       24612 if ( ! defined $type ) {
113 718         1116 my $library = $args{type_library};
114 718         1071 my $name = $args{type_name};
115            
116 718         1545 $qualified_name = "$library\::$name";
117             $coderef = sub (;@) {
118 388     388   2530 my $params;
119 388 50       1111 $params = shift if ref( $_[0] ) eq "ARRAY";
120            
121 388   100     1397 $type ||= do {
122 386 50 0     2788 $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 386         1356 $library->get_type( $name );
126             };
127            
128 388         739 my $t;
129 388 100       915 if ( $type ) {
130 3 50       7 $t = $params ? $type->parameterize( @$params ) : $type;
131 3 50       9 $t = $t->$post_method if $post_method;
132             }
133             else {
134 385 50 0     879 require Error::TypeTiny && Error::TypeTiny::croak( "Cannot parameterize a non-existant type" )
135             if $params;
136 385         19747 require Type::Tiny::_DeclaredType;
137 385         3194 $t = Type::Tiny::_DeclaredType->new( library => $library, name => $name );
138             }
139            
140 388 100 66     2813 @_ && wantarray ? return ( $t, @_ ) : return $t;
141 718         3794 };
142            
143 718 50       6040 require Scalar::Util && &Scalar::Util::set_prototype( $coderef, ';$' )
144             if Eval::TypeTiny::NICE_PROTOTYPES;
145             }
146             else {
147            
148             #<<<
149 12255 100       28236 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 12255         31844 $qualified_name = $type->qualified_name;
170             $coderef = eval_closure(
171             source => $source,
172 12255   33     73620 description => $args{description} || sprintf( "exportable function '%s'", $qualified_name ),
173             environment => { '$type' => \$type },
174             );
175             }
176            
177 12973 50       50215 $args{anonymous} ? $coderef : set_subname( $qualified_name, $coderef );
178             }
179              
180             sub eval_closure {
181 27124     27124 1 110365 my ( %args ) = @_;
182             my $src =
183             ref $args{source} eq "ARRAY"
184 2         17 ? join( "\n", @{ $args{source} } )
185 27124 100       72286 : $args{source};
186            
187 27124 100       71231 $args{alias} = 0 unless defined $args{alias};
188 27124 100       61213 $args{line} = 1 unless defined $args{line};
189             $args{description} =~ s/[^\w .:-\[\]\(\)\{\}\']//g
190 27124 100       97023 if defined $args{description};
191             $src = qq{#line $args{line} "$args{description}"\n$src}
192 27124 100 66     170029 if defined $args{description} && !( $^P & 0x10 );
193 27124   100     93022 $args{environment} ||= {};
194            
195 27124         36741 if ( _EXTENDED_TESTING ) {
196 27124         112847 require Scalar::Util;
197 27124         40658 for my $k ( sort keys %{ $args{environment} } ) {
  27124         105249  
198             next
199             if $k =~ /^\$/
200 12980 100 66     116118 && Scalar::Util::reftype( $args{environment}{$k} ) =~ /^(SCALAR|REF)$/;
201             next
202             if $k =~ /^\@/
203 511 100 66     2276 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(ARRAY);
204             next
205             if $k =~ /^\%/
206 274 100 100     917 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(HASH);
207             next
208             if $k =~ /^\&/
209 267 100 66     699 && 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         507 $args{environment}{$k}
215             );
216             } #/ for my $k ( sort keys %...)
217             } #/ if ( _EXTENDED_TESTING)
218            
219 27123         51347 my $sandpkg = 'Eval::TypeTiny::Sandbox';
220 26859 100       55576 my $alias = exists( $args{alias} ) ? $args{alias} : 0;
221 26859         35467 my @keys = sort keys %{ $args{environment} };
  26859         62393  
222 26859         40531 my $i = 0;
223 27123         88501 my $source = join "\n" => (
224             "package $sandpkg;",
225             "sub {",
226             map( _make_lexical_assignment( $_, $i++, $alias ), @keys ),
227             $src,
228             "}",
229             );
230            
231 27123 100 100     67334 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_TIE ) {
232 269         1718 _manufacture_ties();
233             }
234            
235 27123         53311 my ( $compiler, $e ) = _clean_eval( $source );
236 26859 100       74973 if ( $e ) {
237 4         17 chomp $e;
238 268         1791 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       64 );
246             } #/ if ( $e )
247            
248 26855         46958 my $code = $compiler->( @{ $args{environment} }{@keys} );
  27119         352265  
249 26855         147632 undef( $compiler );
250            
251 26855 100 100     64084 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
252 3         14 require Devel::LexAlias;
253             Devel::LexAlias::lexalias( $code, $_ => $args{environment}{$_} )
254 3         49 for grep !/^\&/, @keys;
255             }
256            
257 26855 100 100     54836 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
258 3         13 require PadWalker;
259 3         22 my %env = map +( $_ => $args{environment}{$_} ), grep !/^\&/, @keys;
260 3         15 PadWalker::set_closed_over( $code, \%env );
261             }
262            
263 26855         143454 return $code;
264             } #/ sub eval_closure
265              
266             my $tmp;
267              
268             sub _make_lexical_assignment {
269 12835     12835   26803 my ( $key, $index, $alias ) = @_;
270 12835         26096 my $name = substr( $key, 1 );
271            
272 12835 100       29847 if ( HAS_LEXICAL_SUBS and $key =~ /^\&/ ) {
273 2         4 $tmp++;
274 2         6 my $tmpname = '$__LEXICAL_SUB__' . $tmp;
275             return
276 2         12 "no warnings 'experimental::lexical_subs';"
277             . "use feature 'lexical_subs';"
278             . "my $tmpname = \$_[$index];"
279             . "my sub $name { goto $tmpname };";
280             }
281            
282 12833 100       23663 if ( !$alias ) {
    100          
    100          
    100          
283 12809         21615 my $sigil = substr( $key, 0, 1 );
284 12809         72561 return "my $key = $sigil\{ \$_[$index] };";
285             }
286             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_NATIVE ) {
287             return
288 7         42 "no warnings 'experimental::refaliasing';"
289             . "use feature 'refaliasing';"
290             . "my $key; \\$key = \$_[$index];";
291             }
292             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
293 5         28 return "my $key;";
294             }
295             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
296 5         32 return "my $key;";
297             }
298             else {
299             my $tieclass = {
300             '@' => 'Eval::TypeTiny::_TieArray',
301             '%' => 'Eval::TypeTiny::_TieHash',
302             '$' => 'Eval::TypeTiny::_TieScalar',
303 7         27 }->{ substr( $key, 0, 1 ) };
304            
305 7         63 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 5 0 66 5   205 $tie ||= eval <<'FALLBACK'; } }
    0 0        
    0 66        
    100          
    100          
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__