diff options
author | Vincent Pit <perl@profvince.com> | 2011-06-25 23:36:50 +0200 |
---|---|---|
committer | Vincent Pit <perl@profvince.com> | 2011-06-26 00:16:29 +0200 |
commit | c08f093b3e154c428f604f89f7feb633e6c97869 (patch) | |
tree | 5b3818b4c6011f1249a3d2a749bdc79fae6586a8 /pp_ctl.c | |
parent | f02ea43cac371ecb59188f9654a0d99fd54db862 (diff) | |
download | perl-c08f093b3e154c428f604f89f7feb633e6c97869.tar.gz |
Correctly preserve the stack on an implicit break.
Perl generates a 'break' op with the special flag set at the end of every
'when' block. This makes it difficult to handle both the case of an
implicit break, where the stack has to be preserved, and the case of an
explicit break, which must obliterate the stack, with the same pp function.
Stack handling should naturally occur in 'leavewhen', but it is effectively
called only when the block issues a 'continue'.
In order to preserve the stack, we change the respective roles of 'break',
'continue' and 'leavewhen' ops :
- Special 'break' ops are no longer generated for implicit breaks. Just as
before, they give the control back to the 'leavegiven' op.
- 'continue' ops now directly forward to the op *following* the 'leavewhen'
op of the current 'when' block.
- 'leavewhen' is now only called at the natural end of a 'when' block.
It adjusts the stack to make sure returned values survive the temp cleanup,
then issues a 'next' or go to the current 'leavegiven' depending on whether
it is enclosed in a for loop or a given block.
This fixes [perl #93548].
Diffstat (limited to 'pp_ctl.c')
-rw-r--r-- | pp_ctl.c | 87 |
1 files changed, 50 insertions, 37 deletions
@@ -4950,7 +4950,7 @@ PP(pp_enterwhen) if ((0 == (PL_op->op_flags & OPf_SPECIAL)) && !SvTRUEx(POPs)) RETURNOP(cLOGOP->op_other->op_next); - ENTER_with_name("eval"); + ENTER_with_name("when"); SAVETMPS; PUSHBLOCK(cx, CXt_WHEN, SP); @@ -4962,43 +4962,70 @@ PP(pp_enterwhen) PP(pp_leavewhen) { dVAR; dSP; + I32 cxix; register PERL_CONTEXT *cx; - I32 gimme __attribute__unused__; + I32 gimme; SV **newsp; PMOP *newpm; + cxix = dopoptogiven(cxstack_ix); + if (cxix < 0) + DIE(aTHX_ "Can't use when() outside a topicalizer"); + POPBLOCK(cx,newpm); assert(CxTYPE(cx) == CXt_WHEN); - SP = newsp; - PUTBACK; - + TAINT_NOT; + SP = adjust_stack_on_leave(newsp, SP, newsp, gimme, SVs_PADTMP|SVs_TEMP); PL_curpm = newpm; /* pop $1 et al */ - LEAVE_with_name("eval"); - return NORMAL; + LEAVE_with_name("when"); + + if (cxix < cxstack_ix) + dounwind(cxix); + + cx = &cxstack[cxix]; + + if (CxFOREACH(cx)) { + /* clear off anything above the scope we're re-entering */ + I32 inner = PL_scopestack_ix; + + TOPBLOCK(cx); + if (PL_scopestack_ix < inner) + leave_scope(PL_scopestack[PL_scopestack_ix]); + PL_curcop = cx->blk_oldcop; + + return cx->blk_loop.my_op->op_nextop; + } + else + /* RETURNOP calls PUTBACK which restores the old old sp */ + return cx->blk_givwhen.leave_op; } PP(pp_continue) { - dVAR; + dVAR; dSP; I32 cxix; register PERL_CONTEXT *cx; - I32 inner; + I32 gimme; + SV **newsp; + PMOP *newpm; cxix = dopoptowhen(cxstack_ix); if (cxix < 0) DIE(aTHX_ "Can't \"continue\" outside a when block"); + if (cxix < cxstack_ix) dounwind(cxix); - /* clear off anything above the scope we're re-entering */ - inner = PL_scopestack_ix; - TOPBLOCK(cx); - if (PL_scopestack_ix < inner) - leave_scope(PL_scopestack[PL_scopestack_ix]); - PL_curcop = cx->blk_oldcop; - return cx->blk_givwhen.leave_op; + POPBLOCK(cx,newpm); + assert(CxTYPE(cx) == CXt_WHEN); + + SP = newsp; + PL_curpm = newpm; /* pop $1 et al */ + + LEAVE_with_name("when"); + RETURNOP(cx->blk_givwhen.leave_op->op_next); } PP(pp_break) @@ -5006,34 +5033,20 @@ PP(pp_break) dVAR; I32 cxix; register PERL_CONTEXT *cx; - I32 inner; - dSP; cxix = dopoptogiven(cxstack_ix); - if (cxix < 0) { - if (PL_op->op_flags & OPf_SPECIAL) - DIE(aTHX_ "Can't use when() outside a topicalizer"); - else - DIE(aTHX_ "Can't \"break\" outside a given block"); - } - if (CxFOREACH(&cxstack[cxix]) && (0 == (PL_op->op_flags & OPf_SPECIAL))) + if (cxix < 0) + DIE(aTHX_ "Can't \"break\" outside a given block"); + + cx = &cxstack[cxix]; + if (CxFOREACH(cx)) DIE(aTHX_ "Can't \"break\" in a loop topicalizer"); if (cxix < cxstack_ix) dounwind(cxix); - - /* clear off anything above the scope we're re-entering */ - inner = PL_scopestack_ix; - TOPBLOCK(cx); - if (PL_scopestack_ix < inner) - leave_scope(PL_scopestack[PL_scopestack_ix]); - PL_curcop = cx->blk_oldcop; - if (CxFOREACH(cx)) - return (cx)->blk_loop.my_op->op_nextop; - else - /* RETURNOP calls PUTBACK which restores the old old sp */ - RETURNOP(cx->blk_givwhen.leave_op); + /* RETURNOP calls PUTBACK which restores the old old sp */ + return cx->blk_givwhen.leave_op; } static MAGIC * |