summaryrefslogtreecommitdiff
path: root/gv.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2013-11-05 06:14:59 -0800
committerFather Chrysostomos <sprout@cpan.org>2013-11-05 12:55:02 -0800
commit8941bf970eeb8009630d57a0b1bacb99f40db9bf (patch)
tree81f1d4eacc8f94d1ec19dce2e51931f0cc0c7cde /gv.c
parent3b927101bf18bfcb197cacec8603d2b8297963ae (diff)
downloadperl-8941bf970eeb8009630d57a0b1bacb99f40db9bf.tar.gz
Stop gv_try_downgrade from anonymising referenced CVs
I keep discovering ways in which gv_try_downgrade, which is supposed to be an optimisation, changes observable behaviour even without look- ing at the stash. This one had me confused at first: $ ./perl -Ilib -e 'use constant foo=>1; BEGIN { $x = \&foo } undef &$x; $x->()' Undefined subroutine called at -e line 1. $ ./perl -Ilib -e 'use constant foo=>1; BEGIN { $x = \&{"foo"} } undef &$x; $x->()' Undefined subroutine &main::foo called at -e line 1. Notice how the first example (where gv_try_downgrade kicks in) shows no name in the error message. This only happens on non- threaded builds. What’s happening is that, when the BEGIN block is freed, the GV op corresponding to &foo get freed, triggering gv_try_downgrade, which checks to see whether it can downgrade the GV to a simple reference to a constant (the way constants are stored by default). It then pro- ceeds to do that, so the GV qua GV ceases to exist, and the CV gets automatically anonymised as a result (the same thing happens with ‘$x = \&{"foo"}; Dump $x; delete $::{foo}’, but legitimately in that case). The solution here is to check the reference count on the CV before downgrading the GV. If the CV’s reference count > 1, then we should leave it alone.
Diffstat (limited to 'gv.c')
-rw-r--r--gv.c2
1 files changed, 1 insertions, 1 deletions
diff --git a/gv.c b/gv.c
index f600942156..97036bc7d7 100644
--- a/gv.c
+++ b/gv.c
@@ -3345,7 +3345,7 @@ Perl_gv_try_downgrade(pTHX_ GV *gv)
if (!cv) {
HEK *gvnhek = GvNAME_HEK(gv);
(void)hv_deletehek(stash, gvnhek, G_DISCARD);
- } else if (GvMULTI(gv) && cv &&
+ } else if (GvMULTI(gv) && cv && SvREFCNT(cv) == 1 &&
!SvOBJECT(cv) && !SvMAGICAL(cv) && !SvREADONLY(cv) &&
CvSTASH(cv) == stash && CvGV(cv) == gv &&
CvCONST(cv) && !CvMETHOD(cv) && !CvLVALUE(cv) && !CvUNIQUE(cv) &&