summaryrefslogtreecommitdiff
path: root/pad.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2014-12-08 21:59:22 -0800
committerFather Chrysostomos <sprout@cpan.org>2014-12-09 05:42:47 -0800
commita70f21d0d169a526a6bafd2465e01e1ca8d16234 (patch)
tree140c9962225a341472bed59fd5669fee314f8419 /pad.c
parent071d040c70fbd1bbb40d7589e2a35485a11508b5 (diff)
downloadperl-a70f21d0d169a526a6bafd2465e01e1ca8d16234.tar.gz
Fix OUTSIDE for named subs inside predeclared subs
This prints 42 as expected (foo initialises $x and bar reads it via eval): use 5.01; sub foo { state $x = 42; sub bar { eval 'print $x // "u", "\n"'; } } foo(); bar(); If you predeclare foo and vivify its glob, use 5.01; *foo; # vivifies the glob at compile time sub foo; sub foo { state $x = 42; sub bar { eval 'print $x // "u", "\n"'; } } foo(); bar(); then the output is ‘u’, because $x is now undefined. What’s happening is that ‘eval’ follows CvOUTSIDE pointers (each sub points to its outer sub), searching each pad to find a lexical $x. In the former case it succeeds. In the latter, bar’s CvOUTSIDE pointer is pointing to the wrong thing, so the search fails and $x is treated as global. You can see it’s global with this example, which prints ‘globular’: use 5.01; *foo; # vivifies the glob at compile time sub foo; sub foo { state $x = 42; sub bar { eval 'print $x // "u", "\n"'; } } foo(); $main::x = "globular"; bar(); When a sub is compiled, a new CV is created at the outset and put in PL_compcv. When the sub finishes compiling, the CV in PL_compcv is installed in the sub’s typeglob (or as a subref in the stash if pos- sible). If there is already a stub in a typeglob, since that stub could be referenced elsewhere, we have to reuse that stub and transfer the contents of PL_compcv to that stub. If we have any subs inside it, those will now have CvOUTSIDE point- ers pointing to the old PL_compcv that has been eviscerated. So we go through the pad and fix up the outside pointers for any subs found there. Named subs don’t get stored in the pad like that, so the CvOUTSIDE fix-up never happens. Hence the bug above. The bug does not occur if the glob is not vivified before the sub def- inition, because a stub declaration will skip creating a real CV if it can. It can’t if there is a typeglob. The solution, of course, is to store named subs in the outer sub’s pad. We can skip this if the outer ‘sub’ is an eval or the main pro- gram. These two types of CVs obviously don’t reuse existing stubs, since they never get installed in the symbol table. Since named subs have strong outside pointers, we have to store weak refs in the pad, just as we do for formats.
Diffstat (limited to 'pad.c')
-rw-r--r--pad.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/pad.c b/pad.c
index 7068b8d336..b8fcf676f8 100644
--- a/pad.c
+++ b/pad.c
@@ -829,6 +829,24 @@ Perl_pad_add_anon(pTHX_ CV* func, I32 optype)
return ix;
}
+void
+Perl_pad_add_weakref(pTHX_ CV* func)
+{
+ const PADOFFSET ix = pad_alloc(OP_NULL, SVs_PADMY);
+ PADNAME * const name = newPADNAMEpvn("&", 1);
+ SV * const rv = newRV_inc((SV *)func);
+
+ PERL_ARGS_ASSERT_PAD_ADD_WEAKREF;
+
+ /* These two aren't used; just make sure they're not equal to
+ * PERL_PADSEQ_INTRO. They should be 0 by default. */
+ assert(COP_SEQ_RANGE_LOW (name) != PERL_PADSEQ_INTRO);
+ assert(COP_SEQ_RANGE_HIGH(name) != PERL_PADSEQ_INTRO);
+ padnamelist_store(PL_comppad_name, ix, name);
+ sv_rvweaken(rv);
+ av_store(PL_comppad, ix, rv);
+}
+
/*
=for apidoc pad_check_dup