X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDBIx%2FClass%2F_Util.pm;h=b5991fb502aef666f138b70931e5644bdd8812b6;hb=439a7283a981f27a56e745d99e456fc50a5a018f;hp=bfb6a2a8c9053b06f48412b533c4e0b72cd948e9;hpb=7302b3e0fadad3321a1e0ad681949b06c9c8601f;p=dbsrgits%2FDBIx-Class.git diff --git a/lib/DBIx/Class/_Util.pm b/lib/DBIx/Class/_Util.pm index bfb6a2a..b5991fb 100644 --- a/lib/DBIx/Class/_Util.pm +++ b/lib/DBIx/Class/_Util.pm @@ -29,15 +29,21 @@ BEGIN { # add an escape for these perls ON SMOKERS - a user will still get death PEEPEENESS => ( eval { DBICTest::RunMode->is_smoker } && ( "$]" >= 5.013005 and "$]" <= 5.013006) ), - SHUFFLE_UNORDERED_RESULTSETS => $ENV{DBIC_SHUFFLE_UNORDERED_RESULTSETS} ? 1 : 0, - - ASSERT_NO_INTERNAL_WANTARRAY => $ENV{DBIC_ASSERT_NO_INTERNAL_WANTARRAY} ? 1 : 0, - - ASSERT_NO_INTERNAL_INDIRECT_CALLS => $ENV{DBIC_ASSERT_NO_INTERNAL_INDIRECT_CALLS} ? 1 : 0, - - STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE => $ENV{DBIC_STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE} ? 1 : 0, - - STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE => $ENV{DBIC_STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE} ? 1 : 0, + ( map + # + # the "DBIC_" prefix below is crucial - this is what makes CI pick up + # all envvars without further adjusting its scripts + # DO NOT CHANGE to the more logical { $_ => !!( $ENV{"DBIC_$_"} ) } + # + { substr($_, 5) => !!( $ENV{$_} ) } + qw( + DBIC_SHUFFLE_UNORDERED_RESULTSETS + DBIC_ASSERT_NO_INTERNAL_WANTARRAY + DBIC_ASSERT_NO_INTERNAL_INDIRECT_CALLS + DBIC_STRESSTEST_UTF8_UPGRADE_GENERATED_COLLAPSER_SOURCE + DBIC_STRESSTEST_COLUMN_INFO_UNAWARE_STORAGE + ) + ), IV_SIZE => $Config{ivsize}, @@ -73,8 +79,10 @@ our @EXPORT_OK = qw( sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt fail_on_internal_wantarray fail_on_internal_call refdesc refcount hrefaddr - scope_guard is_exception detected_reinvoked_destructor + scope_guard detected_reinvoked_destructor + is_exception dbic_internal_try quote_sub qsub perlstring serialize deep_clone + parent_dir mkdir_p UNRESOLVABLE_CONDITION ); @@ -138,9 +146,11 @@ sub scope_guard (&) { eval { $_[0]->[0]->(); 1; - } or do { - Carp::cluck "Execution of scope guard $_[0] resulted in the non-trappable exception:\n\n$@"; - }; + } + or + Carp::cluck( + "Execution of scope guard $_[0] resulted in the non-trappable exception:\n\n$@" + ); } } @@ -223,6 +233,85 @@ sub is_exception ($) { } { + my $callstack_state; + + # Recreate the logic of try(), while reusing the catch()/finally() as-is + # + # FIXME: We need to move away from Try::Tiny entirely (way too heavy and + # yes, shows up ON TOP of profiles) but this is a batle for another maint + sub dbic_internal_try (&;@) { + + my $try_cref = shift; + my $catch_cref = undef; # apparently this is a thing... https://rt.perl.org/Public/Bug/Display.html?id=119311 + + for my $arg (@_) { + + if( ref($arg) eq 'Try::Tiny::Catch' ) { + + croak 'dbic_internal_try() may not be followed by multiple catch() blocks' + if $catch_cref; + + $catch_cref = $$arg; + } + elsif ( ref($arg) eq 'Try::Tiny::Finally' ) { + croak 'dbic_internal_try() does not support finally{}'; + } + else { + croak( + 'dbic_internal_try() encountered an unexpected argument ' + . "'@{[ defined $arg ? $arg : 'UNDEF' ]}' - perhaps " + . 'a missing semi-colon before or ' # trailing space important + ); + } + } + + my $wantarray = wantarray; + my $preexisting_exception = $@; + + my @ret; + my $all_good = eval { + $@ = $preexisting_exception; + + local $callstack_state->{in_internal_try} = 1 + unless $callstack_state->{in_internal_try}; + + # always unset - someone may have snuck it in + local $SIG{__DIE__} + if $SIG{__DIE__}; + + + if( $wantarray ) { + @ret = $try_cref->(); + } + elsif( defined $wantarray ) { + $ret[0] = $try_cref->(); + } + else { + $try_cref->(); + } + + 1; + }; + + my $exception = $@; + $@ = $preexisting_exception; + + if ( $all_good ) { + return $wantarray ? @ret : $ret[0] + } + elsif ( $catch_cref ) { + for ( $exception ) { + return $catch_cref->($exception); + } + } + + return; + } + + sub in_internal_try { !! $callstack_state->{in_internal_try} } +} + +{ my $destruction_registry = {}; sub CLONE { @@ -230,6 +319,11 @@ sub is_exception ($) { { defined $_ ? ( refaddr($_) => $_ ) : () } values %$destruction_registry }; + + # Dummy NEXTSTATE ensuring the all temporaries on the stack are garbage + # collected before leaving this scope. Depending on the code above, this + # may very well be just a preventive measure guarding future modifications + undef; } # This is almost invariably invoked from within DESTROY @@ -316,6 +410,54 @@ sub modver_gt_or_eq_and_lt ($$$) { ) ? 1 : 0; } + +# +# Why not just use some higher-level module or at least File::Spec here? +# Because: +# 1) This is a *very* rarely used function, and the deptree is large +# enough already as it is +# +# 2) (more importantly) Our tooling is utter shit in this area. There +# is no comprehensive support for UNC paths in PathTools and there +# are also various small bugs in representation across different +# path-manipulation CPAN offerings. +# +# Since this routine is strictly used for logical path processing (it +# *must* be able to work with not-yet-existing paths), use this seemingly +# simple but I *think* complete implementation to feed to other consumers +# +# If bugs are ever uncovered in this routine, *YOU ARE URGED TO RESIST* +# the impulse to bring in an external dependency. During runtime there +# is exactly one spot that could potentially maybe once in a blue moon +# use this function. Keep it lean. +# +sub parent_dir ($) { + ( $_[0] =~ m{ [\/\\] ( \.{0,2} ) ( [\/\\]* ) \z }x ) + ? ( + $_[0] + . + ( ( length($1) and ! length($2) ) ? '/' : '' ) + . + '../' + ) + : ( + require File::Spec + and + File::Spec->catpath ( + ( File::Spec->splitpath( "$_[0]" ) )[0,1], + '/', + ) + ) + ; +} + +sub mkdir_p ($) { + require File::Path; + # do not ask for a recent version, use 1.x API calls + File::Path::mkpath([ "$_[0]" ]); # File::Path does not like objects +} + + { my $list_ctx_ok_stack_marker;