summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2012-03-28 21:44:41 -0500
committerJesse Luehrs <doy@tozt.net>2012-11-07 20:28:03 -0600
commit5bbc4d5d842c1afbc106bdd94148920162063f9a (patch)
tree57ec5940fee9a43439a2c45e47aee38ff0176068
parentdea1691deb1529b0ed4d6aa3d0d8e249f071483c (diff)
downloadperl-5bbc4d5d842c1afbc106bdd94148920162063f9a.tar.gz
fix Carp stacktraces after deleting a stash
When a stash is deleted, caller() will return undef in the package slot for any stack level for which the deleted stash was the current package. This made Carp confused in some cases, so fix that.
-rw-r--r--MANIFEST1
-rw-r--r--dist/Carp/lib/Carp.pm36
-rw-r--r--dist/Carp/lib/Carp/Heavy.pm2
-rw-r--r--dist/Carp/t/stash_deletion.t111
4 files changed, 143 insertions, 7 deletions
diff --git a/MANIFEST b/MANIFEST
index e276e34d8b..e206c3160c 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -3068,6 +3068,7 @@ dist/Carp/lib/Carp.pm Error message extension
dist/Carp/Makefile.PL makefile writer for Carp
dist/Carp/t/Carp.t See if Carp works
dist/Carp/t/heavy.t See if Carp::Heavy works
+dist/Carp/t/stash_deletion.t See if Carp handles stash deletion
dist/Carp/t/swash.t See if Carp avoids breaking swash loading
dist/Carp/t/vivify_gv.t See if Carp leaves utf8:: stuff alone
dist/Carp/t/vivify_stash.t See if Carp leaves utf8:: stash alone
diff --git a/dist/Carp/lib/Carp.pm b/dist/Carp/lib/Carp.pm
index ba351ec714..b35ab69578 100644
--- a/dist/Carp/lib/Carp.pm
+++ b/dist/Carp/lib/Carp.pm
@@ -24,7 +24,7 @@ BEGIN {
}
}
-our $VERSION = '1.27';
+our $VERSION = '1.28';
our $MaxEvalLen = 0;
our $Verbose = 0;
@@ -130,7 +130,7 @@ sub caller_info {
= $cgc ? $cgc->($i) : caller($i);
}
- unless ( defined $call_info{pack} ) {
+ unless ( defined $call_info{file} ) {
return ();
}
@@ -232,6 +232,12 @@ sub get_subname {
}
}
+ # this can happen on older perls when the sub (or the stash containing it)
+ # has been deleted
+ if ( !defined( $info->{sub} ) ) {
+ return '__ANON__::__ANON__';
+ }
+
return ( $info->{sub} eq '(eval)' ) ? 'eval {...}' : $info->{sub};
}
@@ -253,9 +259,14 @@ sub long_error_loc {
last;
}
else {
-
- # OK, now I am irritated.
- return 2;
+ # this can happen when the stash has been deleted
+ # in that case, just assume that it's a reasonable place to
+ # stop (the file and line data will still be intact in any
+ # case) - the only issue is that we can't detect if the
+ # deleted package was internal (so don't do that then)
+ # -doy
+ redo unless 0 > --$lvl;
+ last;
}
}
redo if $CarpInternal{$pkg};
@@ -334,7 +345,20 @@ sub short_error_loc {
$i++;
my $caller = $cgc ? $cgc->($i) : caller($i);
- return 0 unless defined($caller); # What happened?
+ if (!defined($caller)) {
+ my @caller = $cgc ? $cgc->($i) : caller($i);
+ if (@caller) {
+ # if there's no package but there is other caller info, then
+ # the package has been deleted - treat this as a valid package
+ # in this case
+ redo if defined($called) && $CarpInternal{$called};
+ redo unless 0 > --$lvl;
+ last;
+ }
+ else {
+ return 0;
+ }
+ }
redo if $Internal{$caller};
redo if $CarpInternal{$caller};
redo if $CarpInternal{$called};
diff --git a/dist/Carp/lib/Carp/Heavy.pm b/dist/Carp/lib/Carp/Heavy.pm
index 5620dc9d74..5b2b4487bd 100644
--- a/dist/Carp/lib/Carp/Heavy.pm
+++ b/dist/Carp/lib/Carp/Heavy.pm
@@ -2,7 +2,7 @@ package Carp::Heavy;
use Carp ();
-our $VERSION = '1.27';
+our $VERSION = '1.28';
1;
diff --git a/dist/Carp/t/stash_deletion.t b/dist/Carp/t/stash_deletion.t
new file mode 100644
index 0000000000..9d1c786f97
--- /dev/null
+++ b/dist/Carp/t/stash_deletion.t
@@ -0,0 +1,111 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 15;
+
+use Carp;
+
+{
+ my $sub = eval <<'EVAL';
+package Die;
+sub {
+#line 1 foo
+ die "blah";
+}
+EVAL
+ ok(!$@);
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+ {
+ no strict 'refs';
+ delete ${'::'}{'Die::'};
+ }
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+}
+
+{
+ my $sub = eval <<'EVAL';
+package Confess;
+sub {
+#line 1 foo
+ Carp::confess("blah");
+}
+EVAL
+ ok(!$@);
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+ {
+ no strict 'refs';
+ delete ${'::'}{'Confess::'};
+ }
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+}
+
+{
+ my $sub = eval <<'EVAL';
+package CroakHelper;
+sub x {
+ Carp::croak("blah");
+}
+package Croak;
+sub {
+#line 1 foo
+ CroakHelper::x();
+}
+EVAL
+ ok(!$@);
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+ {
+ no strict 'refs';
+ delete ${'::'}{'Croak::'};
+ }
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+ {
+ no strict 'refs';
+ delete ${'::'}{'CroakHelper::'};
+ }
+ eval { $sub->() };
+ like($@, qr/^blah at foo line 1/);
+}
+
+{
+ # the amount of information available and how it is displayed varies quite
+ # a bit depending on the version of perl (specifically, what caller returns
+ # in that version), so there is a bit of fiddling around required to handle
+ # that
+ my $unknown_pat = qr/__ANON__::/;
+ $unknown_pat = qr/$unknown_pat|\(unknown\)/
+ if $] < 5.014;
+
+ my $sub = eval <<'EVAL';
+package SubHelper;
+sub x {
+ Carp::confess("blah");
+}
+package Sub;
+sub {
+#line 1 foo
+ SubHelper::x();
+}
+EVAL
+ ok(!$@);
+ eval { $sub->() };
+ unlike($@, qr/$unknown_pat/);
+ {
+ no strict 'refs';
+ delete ${'::'}{'Sub::'};
+ }
+ eval { $sub->() };
+ like($@, qr/$unknown_pat|Sub::/);
+ unlike($@, qr/$unknown_pat.*$unknown_pat/s);
+ {
+ no strict 'refs';
+ delete ${'::'}{'SubHelper::'};
+ }
+ eval { $sub->() };
+ like($@, qr/(?:$unknown_pat|SubHelper::).*(?:$unknown_pat|Sub::)/s);
+}