Subject: | Custom exception classes |
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.
Subject: | patch.diff |
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