sub
_clean_eval {
local
$@;
local
$SIG
{__DIE__};
my
$r
=
eval
$_
[0];
my
$e
= $@;
return
(
$r
,
$e
);
}
BEGIN {
*HAS_LEXICAL_SUBS
= ( $] >= 5.018 ) ?
sub
() { !!1 } :
sub
() { !!0 };
*NICE_PROTOTYPES
= ( $] >= 5.014 ) ?
sub
() { !!1 } :
sub
() { !!0 };
}
sub
_pick_alternative {
my
$ok
= 0;
while
(
@_
) {
my
(
$type
,
$condition
,
$result
) =
splice
@_
, 0, 3;
if
(
$type
eq
'needs'
) {
++
$ok
if
eval
"require $condition; 1"
;
}
elsif
(
$type
eq
'if'
) {
++
$ok
if
$condition
;
}
next
unless
$ok
;
return
ref
(
$result
) eq
'CODE'
?
$result
->() :
ref
(
$result
) eq
'SCALAR'
?
eval
(
$$result
) :
$result
;
}
return
;
}
{
sub
IMPLEMENTATION_DEVEL_LEXALIAS () {
'Devel::LexAlias'
}
sub
IMPLEMENTATION_PADWALKER () {
'PadWalker'
}
sub
IMPLEMENTATION_TIE () {
'tie'
}
sub
IMPLEMENTATION_NATIVE () {
'perl'
}
my
$implementation
;
sub
ALIAS_IMPLEMENTATION () {
$implementation
||= _pick_alternative(
if
=> ( $] ge
'5.022'
) => IMPLEMENTATION_NATIVE,
needs
=>
'Devel::LexAlias'
=> IMPLEMENTATION_DEVEL_LEXALIAS,
needs
=>
'PadWalker'
=> IMPLEMENTATION_PADWALKER,
if
=> !!
1
=> IMPLEMENTATION_TIE,
);
}
sub
_force_implementation {
$implementation
=
shift
;
}
}
BEGIN {
*_EXTENDED_TESTING
=
$ENV
{EXTENDED_TESTING} ?
sub
() { !!1 } :
sub
() { !!0 };
}
our
$AUTHORITY
=
'cpan:TOBYINK'
;
our
$VERSION
=
'2.008000'
;
our
@EXPORT
=
qw( eval_closure )
;
our
@EXPORT_OK
=
qw(
HAS_LEXICAL_SUBS HAS_LEXICAL_VARS ALIAS_IMPLEMENTATION
IMPLEMENTATION_DEVEL_LEXALIAS IMPLEMENTATION_PADWALKER
IMPLEMENTATION_NATIVE IMPLEMENTATION_TIE
set_subname type_to_coderef NICE_PROTOTYPES
)
;
$VERSION
=~
tr
/_//d;
sub
import
{
no
warnings
"redefine"
;
our
@ISA
=
qw( Exporter::Tiny )
;
my
$next
= \
&Exporter::Tiny::import
;
*import
=
$next
;
my
$class
=
shift
;
my
$opts
= {
ref
(
$_
[0] ) ? %{ +
shift
} : () };
$opts
->{into} ||=
scalar
(
caller
);
return
$class
->
$next
(
$opts
,
@_
);
}
{
my
$subname
;
my
%already
;
sub
set_subname ($$) {
$subname
= _pick_alternative(
needs
=>
'Sub::Util'
=> \
q{ \&Sub::Util::set_subname }
,
needs
=>
'Sub::Name'
=> \
q{ \&Sub::Name::subname }
,
if
=> !!
1
=> 0,
)
unless
defined
$subname
;
$subname
and !
$already
{
$_
[1]}++ and
return
&$subname
;
$_
[1];
}
}
sub
type_to_coderef {
my
(
$type
,
%args
) =
@_
;
my
$post_method
=
$args
{post_method} ||
q()
;
my
(
$coderef
,
$qualified_name
);
if
( !
defined
$type
) {
my
$library
=
$args
{type_library};
my
$name
=
$args
{type_name};
$qualified_name
=
"$library\::$name"
;
$coderef
=
sub
(;@) {
my
$params
;
$params
=
shift
if
ref
(
$_
[0] ) eq
"ARRAY"
;
$type
||=
do
{
$library
->can(
'get_type'
)
&& Error::TypeTiny::croak(
"Expected $library to be a type library, but it doesn't seem to be"
);
$library
->get_type(
$name
);
};
my
$t
;
if
(
$type
) {
$t
=
$params
?
$type
->parameterize(
@$params
) :
$type
;
$t
=
$t
->
$post_method
if
$post_method
;
}
else
{
require
Error::TypeTiny && Error::TypeTiny::croak(
"Cannot parameterize a non-existant type"
)
if
$params
;
$t
= Type::Tiny::_DeclaredType->new(
library
=>
$library
,
name
=>
$name
);
}
@_
&&
wantarray
?
return
(
$t
,
@_
) :
return
$t
;
};
require
Scalar::Util &&
&Scalar::Util::set_prototype
(
$coderef
,
';$'
)
if
Eval::TypeTiny::NICE_PROTOTYPES;
}
else
{
my
$source
=
$type
->is_parameterizable ?
sprintf
(
q{
sub (%s) {
if (ref($_[0]) eq 'Type::Tiny::_HalfOp') {
my $complete_type = shift->complete($type);
@_ && wantarray ? return($complete_type, @_) : return $complete_type;
}
my
$params
;
$params
=
shift
if
ref
(
$_
[0]) eq
q(ARRAY)
;
my
$t
=
$params
?
$type
->parameterize(
@$params
) :
$type
;
@_
&&
wantarray
?
return
(
$t
%s
,
@_
) :
return
$t
%s
;
}
},
NICE_PROTOTYPES ?
q(;$)
:
q(;@)
,
$post_method
,
$post_method
,
) :
sprintf
(
q{ sub () { $type%s if $] }
},
$post_method
);
$qualified_name
=
$type
->qualified_name;
$coderef
= eval_closure(
source
=>
$source
,
description
=>
$args
{description} ||
sprintf
(
"exportable function '%s'"
,
$qualified_name
),
environment
=> {
'$type'
=> \
$type
},
);
}
$args
{anonymous} ?
$coderef
: set_subname(
$qualified_name
,
$coderef
);
}
sub
eval_closure {
my
(
%args
) =
@_
;
my
$src
=
ref
$args
{source} eq
"ARRAY"
?
join
(
"\n"
, @{
$args
{source} } )
:
$args
{source};
$args
{alias} = 0
unless
defined
$args
{alias};
$args
{line} = 1
unless
defined
$args
{line};
$args
{description} =~ s/[^\w .:-\[\]\(\)\{\}\']//g
if
defined
$args
{description};
$src
=
qq{#line $args{line}
"$args{description}"
\n
$src
}
if
defined
$args
{description} && !( $^P & 0x10 );
$args
{environment} ||= {};
if
( _EXTENDED_TESTING ) {
for
my
$k
(
sort
keys
%{
$args
{environment} } ) {
next
if
$k
=~ /^\$/
&& Scalar::Util::reftype(
$args
{environment}{
$k
} ) =~ /^(SCALAR|REF)$/;
next
if
$k
=~ /^\@/
&& Scalar::Util::reftype(
$args
{environment}{
$k
} ) eq
q(ARRAY)
;
next
if
$k
=~ /^\%/
&& Scalar::Util::reftype(
$args
{environment}{
$k
} ) eq
q(HASH)
;
next
if
$k
=~ /^\&/
&& Scalar::Util::reftype(
$args
{environment}{
$k
} ) eq
q(CODE)
;
Error::TypeTiny::croak(
"Expected a variable name and ref; got %s => %s"
,
$k
,
$args
{environment}{
$k
}
);
}
}
my
$sandpkg
=
'Eval::TypeTiny::Sandbox'
;
my
$alias
=
exists
(
$args
{alias} ) ?
$args
{alias} : 0;
my
@keys
=
sort
keys
%{
$args
{environment} };
my
$i
= 0;
my
$source
=
join
"\n"
=> (
"package $sandpkg;"
,
"sub {"
,
map
( _make_lexical_assignment(
$_
,
$i
++,
$alias
),
@keys
),
$src
,
"}"
,
);
if
(
$alias
and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_TIE ) {
_manufacture_ties();
}
my
(
$compiler
,
$e
) = _clean_eval(
$source
);
if
(
$e
) {
chomp
$e
;
"Error::TypeTiny::Compilation"
->throw(
code
=> (
ref
$args
{source} eq
"ARRAY"
?
join
(
"\n"
, @{
$args
{source} } ) :
$args
{source}
),
errstr
=>
$e
,
environment
=>
$args
{environment},
);
}
my
$code
=
$compiler
->( @{
$args
{environment} }{
@keys
} );
undef
(
$compiler
);
if
(
$alias
and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
Devel::LexAlias::lexalias(
$code
,
$_
=>
$args
{environment}{
$_
} )
for
grep
!/^\&/,
@keys
;
}
if
(
$alias
and ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
my
%env
=
map
+(
$_
=>
$args
{environment}{
$_
} ),
grep
!/^\&/,
@keys
;
PadWalker::set_closed_over(
$code
, \
%env
);
}
return
$code
;
}
my
$tmp
;
sub
_make_lexical_assignment {
my
(
$key
,
$index
,
$alias
) =
@_
;
my
$name
=
substr
(
$key
, 1 );
if
( HAS_LEXICAL_SUBS and
$key
=~ /^\&/ ) {
$tmp
++;
my
$tmpname
=
'$__LEXICAL_SUB__'
.
$tmp
;
return
"no warnings 'experimental::lexical_subs';"
.
"use feature 'lexical_subs';"
.
"my $tmpname = \$_[$index];"
.
"my sub $name { goto $tmpname };"
;
}
if
( !
$alias
) {
my
$sigil
=
substr
(
$key
, 0, 1 );
return
"my $key = $sigil\{ \$_[$index] };"
;
}
elsif
( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_NATIVE ) {
return
"no warnings 'experimental::refaliasing';"
.
"use feature 'refaliasing';"
.
"my $key; \\$key = \$_[$index];"
;
}
elsif
( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_DEVEL_LEXALIAS ) {
return
"my $key;"
;
}
elsif
( ALIAS_IMPLEMENTATION eq IMPLEMENTATION_PADWALKER ) {
return
"my $key;"
;
}
else
{
my
$tieclass
= {
'@'
=>
'Eval::TypeTiny::_TieArray'
,
'%'
=>
'Eval::TypeTiny::_TieHash'
,
'$'
=>
'Eval::TypeTiny::_TieScalar'
,
}->{
substr
(
$key
, 0, 1 ) };
return
sprintf
(
'tie(my(%s), "%s", $_[%d]);'
,
$key
,
$tieclass
,
$index
,
);
}
}
{
my
$tie
;
sub
_manufacture_ties {
$tie
||=
eval
<<'FALLBACK'; } }
no warnings qw(void once uninitialized numeric);
use Type::Tiny ();
{
package #
Eval::TypeTiny::_TieArray;
require Tie::Array;
our @ISA = qw( Tie::StdArray );
sub TIEARRAY {
my $class = shift;
bless $_[0] => $class;
}
sub AUTOLOAD {
my $self = shift;
my ($method) = (our $AUTOLOAD =~ /(\w+)$/);
defined tied(@$self) and return tied(@$self)->$method(@_);
require Carp;
Carp::croak(qq[Can't call method "$method" on an undefined value]) unless $method eq 'DESTROY';
}
sub can {
my $self = shift;
my $code = $self->SUPER::can(@_)
|| (defined tied(@$self) and tied(@$self)->can(@_));
return $code;
}
__PACKAGE__->Type::Tiny::_install_overloads(
q[bool] => sub { !! tied @{$_[0]} },
q[""] => sub { '' . tied @{$_[0]} },
q[0+] => sub { 0 + tied @{$_[0]} },
);
}
{
package #
Eval::TypeTiny::_TieHash;
require Tie::Hash;
our @ISA = qw( Tie::StdHash );
sub TIEHASH {
my $class = shift;
bless $_[0] => $class;
}
sub AUTOLOAD {
my $self = shift;
my ($method) = (our $AUTOLOAD =~ /(\w+)$/);
defined tied(%$self) and return tied(%$self)->$method(@_);
require Carp;
Carp::croak(qq[Can't call method "$method" on an undefined value]) unless $method eq 'DESTROY';
}
sub can {
my $self = shift;
my $code = $self->SUPER::can(@_)
|| (defined tied(%$self) and tied(%$self)->can(@_));
return $code;
}
__PACKAGE__->Type::Tiny::_install_overloads(
q[bool] => sub { !! tied %{$_[0]} },
q[""] => sub { '' . tied %{$_[0]} },
q[0+] => sub { 0 + tied %{$_[0]} },
);
}
{
package #
Eval::TypeTiny::_TieScalar;
require Tie::Scalar;
our @ISA = qw( Tie::StdScalar );
sub TIESCALAR {
my $class = shift;
bless $_[0] => $class;
}
sub AUTOLOAD {
my $self = shift;
my ($method) = (our $AUTOLOAD =~ /(\w+)$/);
defined tied($$self) and return tied($$self)->$method(@_);
require Carp;
Carp::croak(qq[Can't call method "$method" on an undefined value]) unless $method eq 'DESTROY';
}
sub can {
my $self = shift;
my $code = $self->SUPER::can(@_)
|| (defined tied($$self) and tied($$self)->can(@_));
return $code;
}
__PACKAGE__->Type::Tiny::_install_overloads(
q[bool] => sub { !! tied ${$_[0]} },
q[""] => sub { '' . tied ${$_[0]} },
q[0+] => sub { 0 + tied ${$_[0]} },
);
}
1;
FALLBACK
1;