autodie icon indicating copy to clipboard operation
autodie copied to clipboard

Custom exception classes [rt.cpan.org #73316]

Open toddr opened this issue 5 years ago • 1 comments

Migrated from rt.cpan.org#73316 (status was 'new')

Requestors:

Attachments:

From [email protected] on 2011-12-16 14:02:42 :

Using custom exception classes for autodie fails if the packages are
defined inline and do not reside in a separate file.

I have attached a patch with a testcase as well as a possible fix.

toddr avatar Jan 16 '20 22:01 toddr

Patch:

diff --git a/lib/Fatal.pm b/lib/Fatal.pm
index aabdf78..c314a2a 100755
--- a/lib/Fatal.pm
+++ b/lib/Fatal.pm
@@ -1234,13 +1234,7 @@ sub exception_class { return "autodie::exception" };
             # actually barewords.  As such, we're left doing a string eval
             # to make sure we load our file correctly.
 
-            my $E;
-
-            {
-                local $@;   # We can't clobber $@, it's wrong!
-                eval "require $exception_class"; ## no critic
-                $E = $@;    # Save $E despite ending our local.
-            }
+            my $E = _load_class($exception_class);
 
             # We need quotes around $@ to make sure it's stringified
             # while still in scope.  Without them, we run the risk of
@@ -1256,6 +1250,35 @@ sub exception_class { return "autodie::exception" };
     }
 }
 
+# Smarter loading of exception classes
+
+sub _load_class {
+    my ($class) = @_;
+
+    {
+        no strict 'refs';
+
+        # Handle by far the two most common cases
+        # This is very fast and handles 99% of cases.
+        return if defined ${"${class}::VERSION"};
+        return if defined @{"${class}::ISA"};
+    
+        # Are there any symbol table entries other than other namespaces
+        foreach ( keys %{"${class}::"} ) {
+            next if substr($_, -2, 2) eq '::';
+            return if defined &{"${class}::$_"};
+        }
+    }
+
+    local $@;   # We can't clobber $@, it's wrong!
+    
+    my $pm_file = $class . ".pm";
+    $pm_file =~ s{ (?: :: | ' ) }{/}gx;
+    eval { require $pm_file };
+    
+    return $@;    # Return $E despite ending our local.
+}
+
 # For some reason, dying while replacing our subs doesn't
 # kill our calling program.  It simply stops the loading of
 # autodie and keeps going with everything else.  The _autocroak

toddr avatar Jan 16 '20 23:01 toddr