diff options
author | Father Chrysostomos <sprout@cpan.org> | 2011-06-03 20:06:24 -0700 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2011-06-03 20:06:24 -0700 |
commit | 767eda446920b18c91ad2d91822428141c99301f (patch) | |
tree | b09e6e97b02d1fbd51b456eba16ae55a6aaac4d3 /pp_ctl.c | |
parent | 339c6c60f3062528eca01e318da233bdf2b57ff2 (diff) | |
download | perl-767eda446920b18c91ad2d91822428141c99301f.tar.gz |
[perl #7946] Lvalue subs do not autovivify
This commit makes autovivification work with lvalue subs. It follows
the same technique used by other autovivifiable ops (aelem, helem,
tc.), except that, due to flag constraints, it uses a single flag and
instead checks the op tree at run time to find out what sort of thing
to vivify.
The flag constraints are that these two flags:
#define OPpENTERSUB_HASTARG 32 /* Called from OP tree. */
#define OPpENTERSUB_NOMOD 64 /* Immune to op_lvalue() for :attrlist. */
conflict with these:
#define OPpDEREF (32|64) /* autovivify: Want ref to something: */
#define OPpDEREF_AV 32 /* Want ref to AV. */
#define OPpDEREF_HV 64 /* Want ref to HV. */
#define OPpDEREF_SV (32|64) /* Want ref to SV. */
Renumbering HASTARG and NOMOD is problematic, as there are places in
op.c that change entersubs into rv2cvs, and the entersub and rv2cv
flags would conflict. Setting the flags correctly when changing the
type is hard and would result in subtle bugs if not done perfectly.
Ops like ${...} don’t actually autovivify; it’s the op inside that
does it. In those cases, the parent op is flagged with OPpDEREFed, and
it skips get-magic, as it has already been called by the inner op.
Since entersub is now marked as being an autovivifying op, ${...} in
lvalue context ends up skipping get-magic if there is a foo() inside.
And this affects even regular subs. So pp_leavesub and pp_return have
to call get-magic; hence the new tests in gmagic.t.
Diffstat (limited to 'pp_ctl.c')
-rw-r--r-- | pp_ctl.c | 26 |
1 files changed, 24 insertions, 2 deletions
@@ -2228,8 +2228,24 @@ S_return_lvalues(pTHX_ SV **mark, SV **sp, SV **newsp, I32 gimme, } else *++newsp = &PL_sv_undef; + if (CxLVAL(cx) & OPpENTERSUB_DEREF) { + SvGETMAGIC(TOPs); + if (!SvOK(TOPs)) { + U8 deref_type; + if (cx->blk_sub.retop->op_type == OP_RV2SV) + deref_type = OPpDEREF_SV; + else if (cx->blk_sub.retop->op_type == OP_RV2AV) + deref_type = OPpDEREF_AV; + else { + assert(cx->blk_sub.retop->op_type == OP_RV2HV); + deref_type = OPpDEREF_HV; + } + vivify_ref(TOPs, deref_type); + } + } } else if (gimme == G_ARRAY) { + assert (!(CxLVAL(cx) & OPpENTERSUB_DEREF)); while (++MARK <= SP) { *++newsp = *MARK; TAINT_NOT; /* Each item is independent */ @@ -2245,6 +2261,7 @@ PP(pp_return) bool popsub2 = FALSE; bool clear_errsv = FALSE; bool lval = FALSE; + bool gmagic = FALSE; I32 gimme; SV **newsp; PMOP *newpm; @@ -2287,6 +2304,7 @@ PP(pp_return) popsub2 = TRUE; lval = !!CvLVALUE(cx->blk_sub.cv); retop = cx->blk_sub.retop; + gmagic = CxLVAL(cx) & OPpENTERSUB_DEREF; cxstack_ix++; /* preserve cx entry on stack for use by POPSUB */ break; case CXt_EVAL: @@ -2332,11 +2350,15 @@ PP(pp_return) FREETMPS; *++newsp = sv_mortalcopy(sv); SvREFCNT_dec(sv); + if (gmagic) SvGETMAGIC(sv); } } + else if (SvTEMP(*SP)) { + *++newsp = *SP; + if (gmagic) SvGETMAGIC(*SP); + } else - *++newsp = - SvTEMP(*SP) ? *SP : sv_mortalcopy(*SP); + *++newsp = sv_mortalcopy(*SP); } else *++newsp = sv_mortalcopy(*SP); |