diff options
author | David Mitchell <davem@iabyn.com> | 2010-07-12 20:53:04 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2010-07-14 23:06:18 +0100 |
commit | 803f274831f937654d48f8cf0468521cbf8f5dff (patch) | |
tree | 297f701cf0a8ef3af29be3017402207f1fa62707 /op.c | |
parent | 96bafef935f82644670a19c8ca57886c240cd969 (diff) | |
download | perl-803f274831f937654d48f8cf0468521cbf8f5dff.tar.gz |
protect CvGV weakref with backref
Each CV usually has a pointer, CvGV(cv), back to the GV that corresponds
to the CV's name (or to *foo::__ANON__ for anon CVs). This pointer wasn't
reference counted, to avoid loops. This could leave it dangling if the GV
is deleted.
We fix this by:
For named subs, adding backref magic to the GV, so that when the GV is
freed, it can trigger processing the CV's CvGV field. This processing
consists of: if it looks like the freeing of the GV is about to trigger
freeing of the CV too, set it to NULL; otherwise make it point to
*foo::__ANON__ (and set CvAONON(cv)).
For anon subs, make CvGV a strong reference, i.e. increment the refcnt of
*foo::__ANON__. This doesn't cause a loop, since in this case the
__ANON__ glob doesn't point to the CV. This also avoids dangling pointers
if someone does an explicit 'delete $foo::{__ANON__}'.
Note that there was already some partial protection for CvGV with
commit f1c32fec87699aee2eeb638f44135f21217d2127. This worked by
anonymising any corresponding CV when freeing a stash or stash entry.
This had two drawbacks. First it didn't fix CVs that were anonmous or that
weren't currently pointed to by the GV (e.g. after local *foo), and
second, it caused *all* CVs to get anonymised during cleanup, even the
ones that would have been deleted shortly afterwards anyway. This commit
effectively removes that former commit, while reusing a bit of the
actual anonymising code.
Diffstat (limited to 'op.c')
-rw-r--r-- | op.c | 17 |
1 files changed, 9 insertions, 8 deletions
@@ -5459,7 +5459,7 @@ Perl_cv_undef(pTHX_ CV *cv) LEAVE; } SvPOK_off(MUTABLE_SV(cv)); /* forget prototype */ - CvGV(cv) = NULL; + cvgv_set(cv, NULL); pad_undef(cv); @@ -5476,8 +5476,9 @@ Perl_cv_undef(pTHX_ CV *cv) if (CvISXSUB(cv) && CvXSUB(cv)) { CvXSUB(cv) = NULL; } - /* delete all flags except WEAKOUTSIDE */ - CvFLAGS(cv) &= CVf_WEAKOUTSIDE; + /* delete all flags except WEAKOUTSIDE and ANON, which indicate the + * ref status of CvOUTSIDE and CvGV */ + CvFLAGS(cv) &= (CVf_WEAKOUTSIDE|CVf_ANON); } void @@ -5871,7 +5872,7 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block) } } if (!CvGV(cv)) { - CvGV(cv) = gv; + cvgv_set(cv, gv); CvFILE_set_from_cop(cv, PL_curcop); CvSTASH(cv) = PL_curstash; if (PL_curstash) @@ -6233,7 +6234,9 @@ Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char *filename) mro_method_changed_in(GvSTASH(gv)); /* newXS */ } } - CvGV(cv) = gv; + if (!name) + CvANON_on(cv); + cvgv_set(cv, gv); (void)gv_fetchfile(filename); CvFILE(cv) = (char *)filename; /* NOTE: not copied, as it is expected to be an external constant string */ @@ -6242,8 +6245,6 @@ Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char *filename) if (name) process_special_blocks(name, gv, cv); - else - CvANON_on(cv); return cv; } @@ -6284,7 +6285,7 @@ Perl_newFORM(pTHX_ I32 floor, OP *o, OP *block) } cv = PL_compcv; GvFORM(gv) = cv; - CvGV(cv) = gv; + cvgv_set(cv, gv); CvFILE_set_from_cop(cv, PL_curcop); |