File Coverage

blib/lib/Eval/TypeTiny.pm
Criterion Covered Total %
statement 145 153 94.7
branch 79 98 80.6
condition 37 61 60.6
subroutine 25 25 100.0
pod 4 4 100.0
total 290 341 85.0


line stmt bran cond sub pod time code
1             package Eval::TypeTiny;
2              
3 304     304   644142 use strict;
  304         1196  
  304         32332  
4              
5             sub _clean_eval {
6 33522     33522   51144 local $@;
7 33522         158150 local $SIG{__DIE__};
8 33522     16   9242887 my $r = eval $_[0];
  8     7   2310  
  8     4   47  
  8     6   488  
  3     1   21  
  3     1   382  
  2     1   54  
  2     1   12  
  2     1   4  
  2         203  
  6         18  
  6         72  
  1         76  
  1         6  
  1         3  
  1         175  
  1         8  
  1         2  
  1         55  
  1         6  
  1         37  
  1         158  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
9 33522         2931380 my $e = $@;
10 33522         215073 return ( $r, $e );
11             }
12              
13 304     304   5201 use warnings;
  304         682  
  304         60274  
14              
15             BEGIN {
16 304 50   304   2424 *HAS_LEXICAL_SUBS = ( $] >= 5.018 ) ? sub () { !!1 } : sub () { !!0 };
17 304 50       129533 *NICE_PROTOTYPES = ( $] >= 5.014 ) ? sub () { !!1 } : sub () { !!0 };
18             }
19              
20             sub _pick_alternative {
21 287     287   7631 my $ok = 0;
22 287         1732 while ( @_ ) {
23 287         2179 my ( $type, $condition, $result ) = splice @_, 0, 3;
24 287 100       1288 if ( $type eq 'needs' ) {
    50          
25 284 50       26428 ++$ok if eval "require $condition; 1";
26             }
27             elsif ( $type eq 'if' ) {
28 3 100       10 ++$ok if $condition;
29             }
30 287 100       2685 next unless $ok;
31 286 100       18420 return ref( $result ) eq 'CODE' ? $result->() : ref( $result ) eq 'SCALAR' ? eval( $$result ) : $result;
    50          
32             }
33 1         4 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 455 $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   1385323 $implementation = shift;
58             }
59             }
60              
61             BEGIN {
62 304 100   304   44847 *_EXTENDED_TESTING = $ENV{EXTENDED_TESTING} ? sub() { !!1 } : sub() { !!0 };
63             }
64              
65             our $AUTHORITY = 'cpan:TOBYINK';
66             our $VERSION = '2.010001';
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 304     304   2335 no warnings "redefine";
  304         1087  
  304         760916  
82 10     17   102 our @ISA = qw( Exporter::Tiny );
83 10         81 require Exporter::Tiny;
84 10         493 my $next = \&Exporter::Tiny::import;
85 7         2082 *import = $next;
86 7         19 my $class = shift;
87 7 50       3126 my $opts = { ref( $_[0] ) ? %{ +shift } : () };
  3         22  
88 4   66     42 $opts->{into} ||= scalar( caller );
89 4         194 return $class->$next( $opts, @_ );
90             } #/ sub import
91              
92             {
93             my $subname;
94             my %already; # prevent renaming established functions
95             sub set_subname ($$) {
96 75443 100   75443 1 165174 $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 75443 100 66     900930 $subname and !$already{$_[1]}++ and return &$subname;
102 23293         50038 $_[1];
103             } #/ sub set_subname ($$)
104             }
105              
106             sub type_to_coderef {
107 16513     16513 1 38909 my ( $type, %args ) = @_;
108 16513   50     61869 my $post_method = $args{post_method} || q();
109            
110 16513         30066 my ( $coderef, $qualified_name );
111            
112 16513 100       37435 if ( ! defined $type ) {
113 1108         2165 my $library = $args{type_library};
114 1108         2556 my $name = $args{type_name};
115            
116 1108         3755 $qualified_name = "$library\::$name";
117             $coderef = sub (;@) {
118 580     580   1030253 my $params;
119 580 50       2054 $params = shift if ref( $_[0] ) eq "ARRAY";
120            
121 580   100     1916 $type ||= do {
122 578 50 0     4492 $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 578         2214 $library->get_type( $name );
126             };
127            
128 580         931 my $t;
129 580 100       1396 if ( $type ) {
130 3 50       12 $t = $params ? $type->parameterize( @$params ) : $type;
131 3 50       170 $t = $t->$post_method if $post_method;
132             }
133             else {
134 577 50 0     1385 require Error::TypeTiny && Error::TypeTiny::croak( "Cannot parameterize a non-existant type" )
135             if $params;
136 577         37870 require Type::Tiny::_DeclaredType;
137 577         3834 $t = Type::Tiny::_DeclaredType->new( library => $library, name => $name );
138             }
139            
140 580 100 66     4364 @_ && wantarray ? return ( $t, @_ ) : return $t;
141 1108         7271 };
142            
143 1108 50       10700 require Scalar::Util && &Scalar::Util::set_prototype( $coderef, ';$' )
144             if Eval::TypeTiny::NICE_PROTOTYPES;
145             }
146             else {
147            
148             #<<<
149 15405 100       47651 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 15405         42298 $qualified_name = $type->qualified_name;
170             $coderef = eval_closure(
171             source => $source,
172 15405   33     107505 description => $args{description} || sprintf( "exportable function '%s'", $qualified_name ),
173             environment => { '$type' => \$type },
174             );
175             }
176            
177 16513 50       82798 $args{anonymous} ? $coderef : set_subname( $qualified_name, $coderef );
178             }
179              
180             sub eval_closure {
181 33523     33523 1 470429 my ( %args ) = @_;
182             my $src =
183             ref $args{source} eq "ARRAY"
184 2         12 ? join( "\n", @{ $args{source} } )
185 33523 100       108089 : $args{source};
186            
187 33523 100       110240 $args{alias} = 0 unless defined $args{alias};
188 33523 100       94174 $args{line} = 1 unless defined $args{line};
189             $args{description} =~ s/[^\w .:-\[\]\(\)\{\}\']//g
190 33523 100       158351 if defined $args{description};
191             $src = qq{#line $args{line} "$args{description}"\n$src}
192 33523 100 66     241608 if defined $args{description} && !( $^P & 0x10 );
193 33523   100     136027 $args{environment} ||= {};
194            
195 33523         51163 if ( _EXTENDED_TESTING ) {
196 33523         198590 require Scalar::Util;
197 33523         54671 for my $k ( sort keys %{ $args{environment} } ) {
  33523         146516  
198             next
199             if $k =~ /^\$/
200 16228 100 66     162202 && Scalar::Util::reftype( $args{environment}{$k} ) =~ /^(SCALAR|REF)$/;
201             next
202             if $k =~ /^\@/
203 549 100 66     2068 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(ARRAY);
204             next
205             if $k =~ /^\%/
206 274 100 100     1003 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(HASH);
207             next
208             if $k =~ /^\&/
209 267 100 66     641 && Scalar::Util::reftype( $args{environment}{$k} ) eq q(CODE);
210            
211 1         6 require Error::TypeTiny;
212             Error::TypeTiny::croak(
213             "Expected a variable name and ref; got %s => %s", $k,
214 265         619 $args{environment}{$k}
215             );
216             } #/ for my $k ( sort keys %...)
217             } #/ if ( _EXTENDED_TESTING)
218            
219 33522         66093 my $sandpkg = 'Eval::TypeTiny::Sandbox';
220 33258 100       79615 my $alias = exists( $args{alias} ) ? $args{alias} : 0;
221 33258         47850 my @keys = sort keys %{ $args{environment} };
  33258         88724  
222 33258         53947 my $i = 0;
223 33522         129235 my $source = join "\n" => (
224             "package $sandpkg;",
225             "sub {",
226             map( _make_lexical_assignment( $_, $i++, $alias ), @keys ),
227             $src,
228             "}",
229             );
230            
231 33522 100 100     94037 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_TIE ) {
232 269         1544 _manufacture_ties();
233             }
234            
235 33522         81578 my ( $compiler, $e ) = _clean_eval( $source );
236 33258 100       116562 if ( $e ) {
237 4         12 chomp $e;
238 268         2675 require Error::TypeTiny::Compilation;
239             "Error::TypeTiny::Compilation"->throw(
240             code => (
241 1         11 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 33254         65198 my $code = $compiler->( @{ $args{environment} }{@keys} );
  33518         609001  
249 33254         218443 undef( $compiler );
250            
251 33254 100 100     95939 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
252 3         17 require Devel::LexAlias;
253             Devel::LexAlias::lexalias( $code, $_ => $args{environment}{$_} )
254 3         26 for grep !/^\&/, @keys;
255             }
256            
257 33254 100 100     77132 if ( $alias and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
258 3         16 require PadWalker;
259 3         23 my %env = map +( $_ => $args{environment}{$_} ), grep !/^\&/, @keys;
260 3         14 PadWalker::set_closed_over( $code, \%env );
261             }
262            
263 33254         234659 return $code;
264             } #/ sub eval_closure
265              
266             my $tmp;
267              
268             sub _make_lexical_assignment {
269 16083     16083   38075 my ( $key, $index, $alias ) = @_;
270 16083         39695 my $name = substr( $key, 1 );
271            
272 16083 100       41040 if ( HAS_LEXICAL_SUBS and $key =~ /^\&/ ) {
273 2         3 $tmp++;
274 2         6 my $tmpname = '$__LEXICAL_SUB__' . $tmp;
275             return
276 2         10 "no warnings 'experimental::lexical_subs';"
277             . "use feature 'lexical_subs';"
278             . "my $tmpname = \$_[$index];"
279             . "my sub $name { goto $tmpname };";
280             }
281            
282 16081 100       34458 if ( !$alias ) {
    100          
    100          
    100          
283 16057         32311 my $sigil = substr( $key, 0, 1 );
284 16057         98090 return "my $key = $sigil\{ \$_[$index] };";
285             }
286             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_NATIVE ) {
287             return
288 7         45 "no warnings 'experimental::refaliasing';"
289             . "use feature 'refaliasing';"
290             . "my $key; \\$key = \$_[$index];";
291             }
292             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
293 5         26 return "my $key;";
294             }
295             elsif ( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
296 5         31 return "my $key;";
297             }
298             else {
299             my $tieclass = {
300             '@' => 'Eval::TypeTiny::_TieArray',
301             '%' => 'Eval::TypeTiny::_TieHash',
302             '$' => 'Eval::TypeTiny::_TieScalar',
303 7         37 }->{ 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   221 $tie ||= eval <<'FALLBACK'; } }
    0 0        
    0 0        
    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__