summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pp_ctl.c71
-rw-r--r--t/op/sub.t38
2 files changed, 67 insertions, 42 deletions
diff --git a/pp_ctl.c b/pp_ctl.c
index f26beb90b2..71d18b2a1a 100644
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -2416,8 +2416,9 @@ PP(pp_return)
if (cxix < cxstack_ix)
dounwind(cxix);
- if (CxMULTICALL(&cxstack[cxix])) {
- gimme = cxstack[cxix].blk_gimme;
+ cx = &cxstack[cxix];
+ if (CxMULTICALL(cx)) {
+ gimme = cx->blk_gimme;
if (gimme == G_VOID)
PL_stack_sp = PL_stack_base;
else if (gimme == G_SCALAR) {
@@ -2427,11 +2428,29 @@ PP(pp_return)
return 0;
}
+ if (CxTYPE(cx) == CXt_SUB && !CvLVALUE(cx->blk_sub.cv)) {
+ SV **oldsp = PL_stack_base + cx->blk_oldsp;
+ if (oldsp != MARK) {
+ /* shift return args to base of call stack frame */
+ SSize_t nargs = SP - MARK;
+ if (nargs) {
+ if (cx->blk_gimme == G_ARRAY)
+ Move(MARK + 1, oldsp + 1, nargs, SV**);
+ else if (cx->blk_gimme == G_SCALAR)
+ oldsp[1] = *SP;
+ }
+ PL_stack_sp = oldsp + nargs;
+ }
+ /* fall through to a normal sub exit */
+ return Perl_pp_leavesub(aTHX);
+ }
+
POPBLOCK(cx,newpm);
switch (CxTYPE(cx)) {
case CXt_SUB:
popsub2 = TRUE;
- lval = !!CvLVALUE(cx->blk_sub.cv);
+ assert(CvLVALUE(cx->blk_sub.cv));
+ lval = TRUE;
retop = cx->blk_sub.retop;
cxstack_ix++; /* preserve cx entry on stack for use by POPSUB */
break;
@@ -2465,45 +2484,15 @@ PP(pp_return)
TAINT_NOT;
if (lval) S_return_lvalues(aTHX_ MARK, SP, newsp, gimme, cx, newpm);
else {
- if (gimme == G_SCALAR) {
- if (MARK < SP) {
- if (popsub2) {
- if (cx->blk_sub.cv && CvDEPTH(cx->blk_sub.cv) > 1) {
- if (SvTEMP(TOPs) && SvREFCNT(TOPs) == 1
- && !SvMAGICAL(TOPs)) {
- *++newsp = SvREFCNT_inc(*SP);
- FREETMPS;
- sv_2mortal(*newsp);
- }
- else {
- sv = SvREFCNT_inc(*SP); /* FREETMPS could clobber it */
- FREETMPS;
- *++newsp = sv_mortalcopy(sv);
- SvREFCNT_dec(sv);
- }
- }
- else if (SvTEMP(*SP) && SvREFCNT(*SP) == 1
- && !SvMAGICAL(*SP)) {
- *++newsp = *SP;
- }
- else
- *++newsp = sv_mortalcopy(*SP);
+ if (gimme == G_SCALAR)
+ *++newsp = (MARK < SP) ? sv_mortalcopy(*SP) : &PL_sv_undef;
+ else if (gimme == G_ARRAY) {
+ while (++MARK <= SP) {
+ *++newsp = sv_mortalcopy(*MARK);
+ TAINT_NOT; /* Each item is independent */
}
- else
- *++newsp = sv_mortalcopy(*SP);
- }
- else
- *++newsp = &PL_sv_undef;
- }
- else if (gimme == G_ARRAY) {
- while (++MARK <= SP) {
- *++newsp = popsub2 && SvTEMP(*MARK) && SvREFCNT(*MARK) == 1
- && !SvGMAGICAL(*MARK)
- ? *MARK : sv_mortalcopy(*MARK);
- TAINT_NOT; /* Each item is independent */
- }
- }
- PL_stack_sp = newsp;
+ }
+ PL_stack_sp = newsp;
}
LEAVE;
diff --git a/t/op/sub.t b/t/op/sub.t
index e8a561ad23..d9b7b3c97e 100644
--- a/t/op/sub.t
+++ b/t/op/sub.t
@@ -6,7 +6,7 @@ BEGIN {
set_up_inc('../lib');
}
-plan(tests => 39);
+plan(tests => 55);
sub empty_sub {}
@@ -295,3 +295,39 @@ inside_predeclared(); # run test
::is($@, "outer\n", "RT124156 depth");
::is($destroyed, 1, "RT124156 freed cv");
}
+
+
+# check that return pops extraneous stuff from the stack
+
+sub check_ret {
+ # the extra scopes push contexts and extra SVs on the stack
+ {
+ my @a = map $_ + 20, @_;
+ for ('x') {
+ return if defined $_[0] && $_[0] < 0;
+ }
+ for ('y') {
+ check_ret(1, do { (2,3,4, return @a ? @a[0..$#a] : ()) }, 4.5);
+ }
+ }
+}
+
+is(scalar check_ret(), undef, "check_ret() scalar");
+is(scalar check_ret(5), 25, "check_ret(5) scalar");
+is(scalar check_ret(5,6), 26, "check_ret(5,6) scalar");
+is(scalar check_ret(5,6,7), 27, "check_ret(5,6,7) scalar");
+is(scalar check_ret(5,6,7,8), 28, "check_ret(5,6,7,8) scalar");
+is(scalar check_ret(5,6,7,8,9), 29, "check_ret(5,6,7,8,9) scalar");
+
+is(scalar check_ret(-1), undef, "check_ret(-1) scalar");
+is(scalar check_ret(-1,5), undef, "check_ret(-1,5) scalar");
+
+is(join('-', 10, check_ret()), "10", "check_ret() list");
+is(join('-', 10, check_ret(5)), "10-25", "check_ret(5) list");
+is(join('-', 10, check_ret(5,6)), "10-25-26", "check_ret(5,6) list");
+is(join('-', 10, check_ret(5,6,7)), "10-25-26-27", "check_ret(5,6,7) list");
+is(join('-', 10, check_ret(5,6,7,8)), "10-25-26-27-28", "check_ret(5,6,7,8) list");
+is(join('-', 10, check_ret(5,6,7,8,9)), "10-25-26-27-28-29", "check_ret(5,6,7,8,9) list");
+
+is(join('-', 10, check_ret(-1)), "10", "check_ret(-1) list");
+is(join('-', 10, check_ret(-1,5)), "10", "check_ret(-1,5) list");