Relax overly aggressive exception-well-formedness checks from 84e4e006
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / _Util.pm
index 8d25ec0..32fdf0c 100644 (file)
@@ -4,7 +4,7 @@ package # hide from PAUSE
 use warnings;
 use strict;
 
-use constant SPURIOUS_VERSION_CHECK_WARNINGS => ($] < 5.010 ? 1 : 0);
+use constant SPURIOUS_VERSION_CHECK_WARNINGS => ( "$]" < 5.010 ? 1 : 0);
 
 BEGIN {
   package # hide from pause
@@ -17,16 +17,17 @@ BEGIN {
     # but of course
     BROKEN_FORK => ($^O eq 'MSWin32') ? 1 : 0,
 
-    BROKEN_GOTO => ($] < '5.008003') ? 1 : 0,
+    BROKEN_GOTO => ( "$]" < 5.008003 ) ? 1 : 0,
 
     HAS_ITHREADS => $Config{useithreads} ? 1 : 0,
 
-    # ::Runmode would only be loaded by DBICTest, which in turn implies t/
-    DBICTEST => eval { DBICTest::RunMode->is_author } ? 1 : 0,
+    UNSTABLE_DOLLARAT => ( "$]" < 5.013002 ) ? 1 : 0,
+
+    DBICTEST => $INC{"DBICTest/Util.pm"} ? 1 : 0,
 
     # During 5.13 dev cycle HELEMs started to leak on copy
     # 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) ),
+    PEEPEENESS => ( eval { DBICTest::RunMode->is_smoker } && ( "$]" >= 5.013005 and "$]" <= 5.013006) ),
 
     SHUFFLE_UNORDERED_RESULTSETS => $ENV{DBIC_SHUFFLE_UNORDERED_RESULTSETS} ? 1 : 0,
 
@@ -43,7 +44,7 @@ BEGIN {
     OS_NAME => $^O,
   };
 
-  if ($] < 5.009_005) {
+  if ( "$]" < 5.009_005) {
     require MRO::Compat;
     constant->import( OLD_MRO => 1 );
   }
@@ -71,7 +72,8 @@ use base 'Exporter';
 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 is_exception detected_reinvoked_destructor
+  refdesc refcount hrefaddr
+  scope_guard is_exception detected_reinvoked_destructor
   quote_sub qsub perlstring serialize deep_clone
   UNRESOLVABLE_CONDITION
 );
@@ -117,9 +119,36 @@ sub serialize ($) {
   nfreeze($_[0]);
 }
 
+sub scope_guard (&) {
+  croak 'Calling scope_guard() in void context makes no sense'
+    if ! defined wantarray;
+
+  # no direct blessing of coderefs - DESTROY is buggy on those
+  bless [ $_[0] ], 'DBIx::Class::_Util::ScopeGuard';
+}
+{
+  package #
+    DBIx::Class::_Util::ScopeGuard;
+
+  sub DESTROY {
+    &DBIx::Class::_Util::detected_reinvoked_destructor;
+
+    local $@ if DBIx::Class::_ENV_::UNSTABLE_DOLLARAT;
+
+    eval {
+      $_[0]->[0]->();
+      1;
+    } or do {
+      Carp::cluck "Execution of scope guard $_[0] resulted in the non-trappable exception:\n\n$@";
+    };
+  }
+}
+
+
 sub is_exception ($) {
   my $e = $_[0];
 
+  # FIXME
   # this is not strictly correct - an eval setting $@ to undef
   # is *not* the same as an eval setting $@ to ''
   # but for the sake of simplicity assume the following for
@@ -130,7 +159,10 @@ sub is_exception ($) {
   {
     local $@;
     eval {
-      $not_blank = ($e ne '') ? 1 : 0;
+      # The ne() here is deliberate - a plain length($e), or worse "$e" ne
+      # will entirely obviate the need for the encolsing eval{}, as the
+      # condition we guard against is a missing fallback overload
+      $not_blank = ( $e ne '' );
       1;
     } or $suberror = $@;
   }
@@ -157,7 +189,7 @@ sub is_exception ($) {
       ));
 
       # workaround, keeps spice flowing
-      $not_blank = ("$e" ne '') ? 1 : 0;
+      $not_blank = !!( length $e );
     }
     else {
       # not blessed yet failed the 'ne'... this makes 0 sense...
@@ -165,6 +197,27 @@ sub is_exception ($) {
       die $suberror
     }
   }
+  elsif (
+    # a ref evaluating to '' is definitively a "null object"
+    ( not $not_blank )
+      and
+    length( my $class = ref $e )
+  ) {
+    carp_unique( sprintf(
+      "Objects of external exception class '%s' stringify to '' (the "
+    . 'empty string), implementing the so called null-object-pattern. '
+    . 'Given Perl\'s "globally cooperative" exception handling using this '
+    . 'class of exceptions is extremely dangerous, as it may (and often '
+    . 'does) result in silent discarding of errors. DBIx::Class tries to '
+    . 'work around this as much as possible, but other parts of your '
+    . 'software stack may not be even aware of the problem. Please submit '
+    . 'a bugreport against the distribution containing %s',
+
+      ($class) x 2,
+    ));
+
+    $not_blank = 1;
+  }
 
   return $not_blank;
 }
@@ -260,7 +313,7 @@ sub modver_gt_or_eq_and_lt ($$$) {
     }
 
     my $cf = 1;
-    while ( ( (caller($cf+1))[3] || '' ) =~ / :: (?:
+    while ( ( (CORE::caller($cf+1))[3] || '' ) =~ / :: (?:
 
       # these are public API parts that alter behavior on wantarray
       search | search_related | slice | search_literal
@@ -278,8 +331,8 @@ sub modver_gt_or_eq_and_lt ($$$) {
     my ($fr, $want, $argdesc);
     {
       package DB;
-      $fr = [ caller($cf) ];
-      $want = ( caller($cf-1) )[5];
+      $fr = [ CORE::caller($cf) ];
+      $want = ( CORE::caller($cf-1) )[5];
       $argdesc = ref $DB::args[0]
         ? DBIx::Class::_Util::refdesc($DB::args[0])
         : 'non '
@@ -305,7 +358,7 @@ sub fail_on_internal_call {
   my ($fr, $argdesc);
   {
     package DB;
-    $fr = [ caller(1) ];
+    $fr = [ CORE::caller(1) ];
     $argdesc = ref $DB::args[0]
       ? DBIx::Class::_Util::refdesc($DB::args[0])
       : undef