summaryrefslogtreecommitdiff
path: root/pp_ctl.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2022-07-06 11:14:22 +0100
committerDavid Mitchell <davem@iabyn.com>2022-07-06 11:23:56 +0100
commit58cf04199f69d7a775bc88024df0bbb48712ea37 (patch)
treef2bc81d1e542b17668b5b4ad6bc7493295f27378 /pp_ctl.c
parent768686e95ac39587f0b8106b607659c4c66bb3f4 (diff)
downloadperl-58cf04199f69d7a775bc88024df0bbb48712ea37.tar.gz
goto(&xs_sub): provide correct caller context
GH #19188 in something like sub foo { goto &xs_sub } if the XS subroutine tries to get its caller context using GIMME_V, it used to always be reported as scalar, since the OP_GOTO is the current op being executed, and that op is marked as scalar. The correct answer should be to return the context of the caller of foo(), but that information is lost as, just before calling the XS sub, pp_goto pops the CXt_SUB off the context stack which holds the recorded value of the context of the caller. The fix in principle is for goto, just before calling the XS sub, to grab the gimme value from the about-to-be-popped CXt_SUB frame, and set OP_GOTO op's op_flag field with that gimme value. However, modifying ops at runtime isn't allowed, as ops are shared between threads. So instead set up a fake copy of the OP_GOTO and alter the gimme value in that instead. I'm pretty confident that on an exception, a LEAVE_SCOPE() will always be done before the JMPENV_JUMP(), so PL_op will be restored to its original value *before* the CPU stack is restored to it's old value (leaving the temporary OP_GOTO as garbage in an abandoned stack frame).
Diffstat (limited to 'pp_ctl.c')
-rw-r--r--pp_ctl.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/pp_ctl.c b/pp_ctl.c
index d34b8d2393..443f4176ac 100644
--- a/pp_ctl.c
+++ b/pp_ctl.c
@@ -2988,6 +2988,7 @@ PP(pp_goto)
const SSize_t items = arg ? AvFILL(arg) + 1 : 0;
const bool m = arg ? cBOOL(SvRMAGICAL(arg)) : 0;
SV** mark;
+ UNOP fake_goto_op;
ENTER;
SAVETMPS;
@@ -3024,6 +3025,20 @@ PP(pp_goto)
PL_comppad = cx->blk_sub.prevcomppad;
PL_curpad = LIKELY(PL_comppad) ? AvARRAY(PL_comppad) : NULL;
+ /* Make a temporary a copy of the current GOTO op on the C
+ * stack, but with a modified gimme (we can't modify the
+ * real GOTO op as that's not thread-safe). This allows XS
+ * users of GIMME_V to get the correct calling context,
+ * even though there is no longer a CXt_SUB frame to
+ * provide that information.
+ */
+ Copy(PL_op, &fake_goto_op, 1, UNOP);
+ fake_goto_op.op_flags =
+ (fake_goto_op.op_flags & ~OPf_WANT)
+ | (cx->blk_gimme & G_WANT);
+ SAVEOP();
+ PL_op = (OP*)&fake_goto_op;
+
/* XS subs don't have a CXt_SUB, so pop it;
* this is a cx_popblock(), less all the stuff we already did
* for cx_topblock() earlier */