diff --git a/lib/Specio/Coercion.pm b/lib/Specio/Coercion.pm index 05387c61..7be9ef98 100644 --- a/lib/Specio/Coercion.pm +++ b/lib/Specio/Coercion.pm @@ -74,6 +74,27 @@ sub inline_coercion { return $self->_inline_generator->( $self, @_ ); } +{ + my $counter = 0; + sub inline_coercion_always { + my $self = shift; + my $value_var = shift; + my $source; + my %env; + if ($self->can_be_inlined) { + $source = $self->inline_coercion($value_var); + %env = %{ $self->inline_environment }; + } + else { + $counter++; + my $coercion_var_name = '$_Specio_Coercion_coercion'.$counter; + $env{$coercion_var_name} = \$self; + $source = $coercion_var_name . '->coerce(' . $value_var . ')'; + } + return ($source, \%env); + } +} + sub _build_optimized_coercion { my $self = shift; diff --git a/lib/Specio/Constraint/Role/Interface.pm b/lib/Specio/Constraint/Role/Interface.pm index 6a92e465..54182382 100644 --- a/lib/Specio/Constraint/Role/Interface.pm +++ b/lib/Specio/Constraint/Role/Interface.pm @@ -319,9 +319,6 @@ sub can_inline_coercion_and_check { sub inline_coercion { my $self = shift; - die 'Cannot inline coercion' - unless $self->can_inline_coercion; - my %env; my $arg_name = $_[0]; @@ -330,16 +327,15 @@ sub inline_coercion { $source .= 'my $value = ' . $arg_name . ';'; $arg_name = '$value'; for my $coercion ( $self->coercions ) { - $source - .= '$value = ' - . $coercion->inline_coercion($arg_name) . ' if ' - . $coercion->from->inline_check($arg_name) . ';'; + my ($coerce, $coerce_env) = $coercion->inline_coercion_always($arg_name); + my ($from, $from_env) = $coercion->from->inline_check_always($arg_name); - %env = ( %env, %{ $coercion->inline_environment } ); + $source .= '$value = ' . $coerce . ' if ' . $from . ';'; + %env = ( %env, %$coerce_env, %$from_env ); } } - $source .= $arg_name . '};'; + $source .= $arg_name . '}'; return ( $source, \%env ); } @@ -347,9 +343,6 @@ sub inline_coercion { sub inline_coercion_and_check { my $self = shift; - die 'Cannot inline coercion and check' - unless $self->can_inline_coercion_and_check; - my %env; my $arg_name = $_[0]; @@ -358,52 +351,33 @@ sub inline_coercion_and_check { $source .= 'my $value = ' . $arg_name . ';'; $arg_name = '$value'; for my $coercion ( $self->coercions ) { - $source - .= '$value = ' - . $coercion->inline_coercion($arg_name) . ' if ' - . $coercion->from->inline_check($arg_name) . ';'; + my ($coerce, $coerce_env) = $coercion->inline_coercion_always($arg_name); + my ($from, $from_env) = $coercion->from->inline_check_always($arg_name); - %env = ( %env, %{ $coercion->inline_environment } ); + $source .= '$value = ' . $coerce . ' if ' . $from . ';'; + %env = ( %env, %$coerce_env, %$from_env ); } } my ( $assert, $assert_env ) = $self->inline_assert($arg_name); - $source .= $assert; + $source .= $assert . ';'; %env = ( %env, %{$assert_env} ); - $source .= $arg_name . '};'; + $source .= $arg_name . '}'; return ( $source, \%env ); } -{ - my $counter = 1; - - sub inline_assert { - my $self = shift; - - my $type_var_name = '$_Specio_Constraint_Interface_type' . $counter; - my $message_generator_var_name - = '$_Specio_Constraint_Interface_message_generator' . $counter; - my %env = ( - $type_var_name => \$self, - $message_generator_var_name => \( $self->_message_generator ), - %{ $self->inline_environment }, - ); +sub inline_assert { + my $self = shift; - my $source = $self->inline_check( $_[0] ); - $source .= ' or '; - $source .= $self->_inline_throw_exception( - $_[0], - $message_generator_var_name, - $type_var_name - ); - $source .= ';'; + my ($check, $check_env) = $self->inline_check_always( $_[0] ); + my ($throw, $throw_env) = $self->_inline_throw_exception( $_[0] ); - $counter++; - - return ( $source, \%env ); - } + return ( + $check . ' or ' . $throw, + { %$check_env, %$throw_env }, + ); } sub inline_check { @@ -415,68 +389,70 @@ sub inline_check { return $type->_inline_generator->( $type, @_ ); } +{ + my $counter = 0; + sub inline_check_always { + my $self = shift; + my $value_var = shift; + my $source; + my %env; + if ($self->can_be_inlined) { + $source = $self->inline_check($value_var); + %env = %{ $self->inline_environment }; + } + else { + $counter++; + my $type_var_name = '$_Specio_Constraint_Role_Interface_type'.$counter; + $env{$type_var_name} = \$self; + $source = $type_var_name . '->value_is_valid(' . $value_var . ')'; + } + return ($source, \%env); + } +} + sub _subify { my $self = shift; - if ( defined &Sub::Quote::quote_sub && $self->can_be_inlined ) { - my $inline = $self->inline_check('$_[0]'); - $inline .= ' or '; - - my %env = ( - '$message_generator' => \( $self->_message_generator ), - '$type' => \$self, - ); - - $inline .= $self->_inline_throw_exception( - '$_[0]', - '$message_generator', - '$type', - ); - - return Sub::Quote::quote_sub( $inline, \%env ); + if ( defined &Sub::Quote::quote_sub) { + return Sub::Quote::quote_sub($self->inline_assert('$_[0]')); } else { return sub { $self->validate_or_die( $_[0] ) }; } } -sub _inline_throw_exception { - my $self = shift; - my $value_var = shift; - my $message_generator_var_name = shift; - my $type_var_name = shift; +{ + my $counter = 0; + sub _inline_throw_exception { + my $self = shift; + my $value_var = shift; + + $counter++; + my $type_var_name = '$_Specio_Constraint_Role_Interface_throw_type'.$counter; + my $message_generator_var_name = '$_Specio_Constraint_Role_Interface_throw_message_generator'.$counter; + + #<<< + my $source + = 'Specio::Exception->throw( ' + . ' message => ' . $message_generator_var_name . '->(' . $value_var . '),' + . ' type => ' . $type_var_name . ',' + . ' value => ' . $value_var . ' )'; + #>>> + my %env = ( + $type_var_name => \$self, + $message_generator_var_name => \( $self->_message_generator ), + ); - #<<< - return 'Specio::Exception->throw( ' - . ' message => ' . $message_generator_var_name . '->(' . $value_var . '),' - . ' type => ' . $type_var_name . ',' - . ' value => ' . $value_var . ' )'; - #>>> + return ($source, \%env); + } } # This exists for the benefit of Moo sub coercion_sub { my $self = shift; - if ( defined &Sub::Quote::quote_sub - && all { $_->can_be_inlined } $self->coercions ) { - - my $inline = q{}; - my %env; - - for my $coercion ( $self->coercions ) { - $inline .= sprintf( - '$_[0] = %s if %s;' . "\n", - $coercion->inline_coercion('$_[0]'), - $coercion->from->inline_check('$_[0]') - ); - - %env = ( %env, %{ $coercion->inline_environment } ); - } - - $inline .= sprintf( "%s;\n", '$_[0]' ); - - return Sub::Quote::quote_sub( $inline, \%env ); + if ( defined &Sub::Quote::quote_sub) { + return Sub::Quote::quote_sub($self->inline_coercion('$_[0]')); } else { return sub { $self->coerce_value(shift) };