+ ud->sv = sv;
+ ud->val = val ? newSVsv(deref ? SvRV(val) : val) : NULL;
+ ud->elem = SvREFCNT_inc(elem);
+
+ return size;
+}
+
+static void su_localize(pTHX_ void *ud_) {
+#define su_localize(U) su_localize(aTHX_ (U))
+ su_ud_localize *ud = (su_ud_localize *) ud_;
+ SV *sv = ud->sv;
+ SV *val = ud->val;
+ SV *elem = ud->elem;
+ svtype t = SU_UD_PRIVATE(ud);
+ GV *gv;
+
+ if (SvTYPE(sv) >= SVt_PVGV) {
+ gv = (GV *) sv;
+ } else {
+#ifdef gv_fetchsv
+ gv = gv_fetchsv(sv, GV_ADDMULTI, t);
+#else
+ STRLEN len;
+ const char *name = SvPV_const(sv, len);
+ gv = gv_fetchpvn_flags(name, len, GV_ADDMULTI, t);
+#endif
+ }
+
+ SU_D({
+ SV *z = newSV(0);
+ SvUPGRADE(z, t);
+ PerlIO_printf(Perl_debug_log, "%p: === localize a %s\n",ud, sv_reftype(z, 0));
+ PerlIO_printf(Perl_debug_log,
+ "%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
+ ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix);
+ SvREFCNT_dec(z);
+ });
+
+ /* Inspired from Alias.pm */
+ switch (t) {
+ case SVt_PVAV:
+ if (elem) {
+ su_save_aelem(GvAV(gv), elem, val);
+ return;
+ } else
+ save_ary(gv);
+ break;
+ case SVt_PVHV:
+ if (elem) {
+ su_save_helem(GvHV(gv), elem, val);
+ return;
+ } else
+ save_hash(gv);
+ break;
+ case SVt_PVGV:
+ save_gp(gv, 1); /* hide previous entry in symtab */
+ break;
+ case SVt_PVCV:
+ su_save_gvcv(gv);
+ break;
+ default:
+ gv = (GV *) save_scalar(gv);
+ break;
+ }
+
+ if (val)
+ SvSetMagicSV((SV *) gv, val);
+
+ return;
+}
+
+/* ... Unique context ID ................................................... */
+
+typedef struct {
+ su_ud_common ci;
+ su_uid *uid;
+} su_ud_uid;
+
+#define SU_UD_UID_UID(U) (((su_ud_uid *) (U))->uid)
+
+static void su_uid_drop(pTHX_ void *ud_) {
+ su_uid *uid = ud_;
+
+ uid->flags &= ~SU_UID_ACTIVE;
+
+ return;
+}
+
+/* --- Pop a context back -------------------------------------------------- */
+
+#ifdef DEBUGGING
+# define SU_CXNAME(C) PL_block_type[CxTYPE(C)]
+#else
+# if SU_HAS_PERL(5, 11, 0)
+static const char *su_block_type[] = {
+ "NULL",
+ "WHEN",
+ "BLOCK",
+ "GIVEN",
+ "LOOP_FOR",
+ "LOOP_PLAIN",
+ "LOOP_LAZYSV",
+ "LOOP_LAZYIV",
+ "SUB",
+ "FORMAT",
+ "EVAL",
+ "SUBST"
+};
+# elif SU_HAS_PERL(5, 9, 3)
+static const char *su_block_type[] = {
+ "NULL",
+ "SUB",
+ "EVAL",
+ "WHEN",
+ "SUBST",
+ "BLOCK",
+ "FORMAT",
+ "GIVEN",
+ "LOOP_FOR",
+ "LOOP_PLAIN",
+ "LOOP_LAZYSV",
+ "LOOP_LAZYIV"
+};
+# else
+static const char *su_block_type[] = {
+ "NULL",
+ "SUB",
+ "EVAL",
+ "LOOP",
+ "SUBST",
+ "BLOCK"
+};
+# endif
+# define SU_CXNAME(C) su_block_type[CxTYPE(C)]
+#endif
+
+static void su_pop(pTHX_ void *ud) {
+#define su_pop(U) su_pop(aTHX_ (U))
+ I32 depth, base, mark, *origin;
+ depth = SU_UD_DEPTH(ud);
+
+ SU_D(
+ PerlIO_printf(Perl_debug_log,
+ "%p: --- pop a %s\n"
+ "%p: leave scope at depth=%2d scope_ix=%2d cur_top=%2d cur_base=%2d\n",
+ ud, SU_CXNAME(cxstack + cxstack_ix),
+ ud, depth, PL_scopestack_ix,PL_savestack_ix,PL_scopestack[PL_scopestack_ix])
+ );
+
+ origin = SU_UD_ORIGIN(ud);
+ mark = origin[depth];
+ base = origin[depth - 1];
+
+ SU_D(PerlIO_printf(Perl_debug_log,
+ "%p: original scope was %*c top=%2d base=%2d\n",
+ ud, 24, ' ', mark, base));
+
+ if (base < mark) {
+#if SU_HAS_PERL(5, 19, 4)
+ I32 save = -1;
+ PERL_CONTEXT *cx;
+#endif
+
+ SU_D(PerlIO_printf(Perl_debug_log, "%p: clear leftovers\n", ud));
+
+#if SU_HAS_PERL(5, 19, 4)
+ cx = cxstack + cxstack_ix;
+ if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
+ save = PL_scopestack[cx->blk_oldscopesp - 1];
+#endif
+
+ PL_savestack_ix = mark;
+ leave_scope(base);
+
+#if SU_HAS_PERL(5, 19, 4)
+ if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
+ PL_scopestack[cx->blk_oldscopesp - 1] = save;
+#endif
+ }
+ PL_savestack_ix = base;
+
+ SU_UD_DEPTH(ud) = --depth;
+
+ if (depth > 0) {
+ U8 pad;
+
+ if ((pad = SU_UD_PAD(ud)) > 0) {
+ dMY_CXT;
+ do {
+ SU_D(PerlIO_printf(Perl_debug_log,
+ "%p: push a pad slot at depth=%2d scope_ix=%2d save_ix=%2d\n",
+ ud, depth, PL_scopestack_ix, PL_savestack_ix));
+ SU_SAVE_PLACEHOLDER();
+ } while (--pad);
+ }
+
+ SU_D(PerlIO_printf(Perl_debug_log,
+ "%p: push destructor at depth=%2d scope_ix=%2d save_ix=%2d\n",
+ ud, depth, PL_scopestack_ix, PL_savestack_ix));
+ SAVEDESTRUCTOR_X(su_pop, ud);
+ } else {
+ switch (SU_UD_TYPE(ud)) {
+ case SU_UD_TYPE_REAP: {
+ SU_D({
+ PerlIO_printf(Perl_debug_log,
+ "%p: === reap\n%p: depth=%2d scope_ix=%2d save_ix=%2d\n",
+ ud, ud, SU_UD_DEPTH(ud), PL_scopestack_ix, PL_savestack_ix);
+ });
+ SAVEDESTRUCTOR_X(su_call, SU_UD_REAP_CB(ud));
+ SU_UD_FREE(ud);
+ break;
+ }
+ case SU_UD_TYPE_LOCALIZE:
+ su_localize(ud);
+ SU_UD_LOCALIZE_FREE(ud);
+ break;
+ case SU_UD_TYPE_UID:
+ SAVEDESTRUCTOR_X(su_uid_drop, SU_UD_UID_UID(ud));
+ SU_UD_FREE(ud);
+ break;
+ }
+ }
+
+ SU_D(PerlIO_printf(Perl_debug_log,
+ "%p: --- end pop: cur_top=%2d == cur_base=%2d\n",
+ ud, PL_savestack_ix, PL_scopestack[PL_scopestack_ix]));
+}
+
+/* --- Initialize the stack and the action userdata ------------------------ */
+
+static I32 su_init(pTHX_ void *ud, I32 cxix, I32 size) {
+#define su_init(U, C, S) su_init(aTHX_ (U), (C), (S))
+ I32 i, depth, offset, base, *origin;
+ U8 pad;
+
+ SU_D(PerlIO_printf(Perl_debug_log, "%p: ### init for cx %d\n", ud, cxix));
+
+ if (size <= SU_SAVE_DESTRUCTOR_SIZE)
+ pad = 0;
+ else {
+ I32 extra = size - SU_SAVE_DESTRUCTOR_SIZE;
+ pad = extra / SU_SAVE_PLACEHOLDER_SIZE;
+ if (extra % SU_SAVE_PLACEHOLDER_SIZE)
+ ++pad;
+ }
+ offset = SU_SAVE_DESTRUCTOR_SIZE + SU_SAVE_PLACEHOLDER_SIZE * pad;
+
+ SU_D(PerlIO_printf(Perl_debug_log, "%p: size=%d pad=%d offset=%d\n",
+ ud, size, pad, offset));
+
+ depth = PL_scopestack_ix - cxstack[cxix].blk_oldscopesp;
+ SU_D(PerlIO_printf(Perl_debug_log, "%p: going down to depth %d\n", ud, depth));
+
+ Newx(origin, depth + 1, I32);
+ base = PL_scopestack_ix - depth;
+ origin[0] = PL_scopestack[base];
+ PL_scopestack[base] += size;
+ for (i = 1; i < depth; ++i) {
+ I32 j = i + base;
+ origin[i] = PL_scopestack[j];
+ PL_scopestack[j] += offset;
+ }
+ origin[depth] = PL_savestack_ix;
+
+ SU_UD_PAD(ud) = pad;
+ SU_UD_DEPTH(ud) = depth;
+ SU_UD_ORIGIN(ud) = origin;
+
+ /* Make sure the first destructor fires by pushing enough fake slots on the
+ * stack. */
+ if (PL_savestack_ix + SU_SAVE_DESTRUCTOR_SIZE
+ <= PL_scopestack[PL_scopestack_ix - 1]) {
+ dMY_CXT;
+ do {
+ SU_D(PerlIO_printf(Perl_debug_log,
+ "%p: push a fake slot at scope_ix=%2d save_ix=%2d\n",
+ ud, PL_scopestack_ix, PL_savestack_ix));
+ SU_SAVE_PLACEHOLDER();
+ } while (PL_savestack_ix + SU_SAVE_DESTRUCTOR_SIZE
+ <= PL_scopestack[PL_scopestack_ix - 1]);
+ }
+ SU_D(PerlIO_printf(Perl_debug_log,
+ "%p: push first destructor at scope_ix=%2d save_ix=%2d\n",
+ ud, PL_scopestack_ix, PL_savestack_ix));
+ SAVEDESTRUCTOR_X(su_pop, ud);
+
+ SU_D({
+ for (i = 0; i <= depth; ++i) {
+ I32 j = PL_scopestack_ix - i;
+ PerlIO_printf(Perl_debug_log,
+ "%p: depth=%2d scope_ix=%2d saved_floor=%2d new_floor=%2d\n",
+ ud, i, j, origin[depth - i],
+ i == 0 ? PL_savestack_ix : PL_scopestack[j]);
+ }
+ });
+
+ return depth;
+}
+
+/* --- Unwind stack -------------------------------------------------------- */
+
+static void su_unwind(pTHX_ void *ud_) {
+ dMY_CXT;
+ I32 cxix = MY_CXT.unwind_storage.cxix;
+ I32 items = MY_CXT.unwind_storage.items;
+ I32 mark;
+
+ PERL_UNUSED_VAR(ud_);
+
+ PL_stack_sp = MY_CXT.unwind_storage.savesp;
+#if SU_HAS_PERL(5, 19, 4)
+ {
+ I32 i;
+ SV **sp = PL_stack_sp;
+ for (i = -items + 1; i <= 0; ++i)
+ if (!SvTEMP(sp[i]))
+ sv_2mortal(SvREFCNT_inc(sp[i]));
+ }
+#endif
+
+ if (cxstack_ix > cxix)
+ dounwind(cxix);
+
+ mark = PL_markstack[cxstack[cxix].blk_oldmarksp];
+ *PL_markstack_ptr = PL_stack_sp - PL_stack_base - items;
+
+ SU_D({
+ I32 gimme = GIMME_V;
+ PerlIO_printf(Perl_debug_log,
+ "%p: cx=%d gimme=%s items=%d sp=%d oldmark=%d mark=%d\n",
+ &MY_CXT, cxix,
+ gimme == G_VOID ? "void" : gimme == G_ARRAY ? "list" : "scalar",
+ items, PL_stack_sp - PL_stack_base, *PL_markstack_ptr, mark);
+ });
+
+ PL_op = (OP *) &(MY_CXT.unwind_storage.return_op);
+ PL_op = PL_op->op_ppaddr(aTHX);
+
+ *PL_markstack_ptr = mark;
+
+ MY_CXT.unwind_storage.proxy_op.op_next = PL_op;
+ PL_op = &(MY_CXT.unwind_storage.proxy_op);
+}
+
+/* --- Yield --------------------------------------------------------------- */
+
+#if SU_HAS_PERL(5, 10, 0)
+# define SU_RETOP_SUB(C) ((C)->blk_sub.retop)
+# define SU_RETOP_EVAL(C) ((C)->blk_eval.retop)
+# define SU_RETOP_LOOP(C) ((C)->blk_loop.my_op->op_lastop->op_next)
+# define SU_RETOP_GIVEN(C) ((C)->blk_givwhen.leave_op->op_next)
+#else
+# define SU_RETOP_SUB(C) ((C)->blk_oldretsp > 0 ? PL_retstack[(C)->blk_oldretsp - 1] : NULL)
+# define SU_RETOP_EVAL(C) SU_RETOP_SUB(C)
+# define SU_RETOP_LOOP(C) ((C)->blk_loop.last_op->op_next)
+#endif
+
+static void su_yield(pTHX_ void *ud_) {
+ dMY_CXT;
+ PERL_CONTEXT *cx;
+ const char *which = ud_;
+ I32 cxix = MY_CXT.yield_storage.cxix;
+ I32 items = MY_CXT.yield_storage.items;
+ opcode type = OP_NULL;
+ U8 flags = 0;
+ OP *next;
+
+ PERL_UNUSED_VAR(ud_);
+
+ cx = cxstack + cxix;
+ switch (CxTYPE(cx)) {
+ case CXt_BLOCK: {
+ I32 i, cur = cxstack_ix, n = 1;
+ OP *o = NULL;
+ /* Is this actually a given/when block? This may occur only when yield was
+ * called with HERE (or nothing) as the context. */
+#if SU_HAS_PERL(5, 10, 0)
+ if (cxix > 0) {
+ PERL_CONTEXT *prev = cx - 1;
+ U8 prev_type = CxTYPE(prev);
+ if ((prev_type == CXt_GIVEN || prev_type == CXt_WHEN)
+ && (prev->blk_oldcop == cx->blk_oldcop)) {
+ cxix--;
+ cx = prev;
+ if (prev_type == CXt_GIVEN)
+ goto cxt_given;
+ else
+ goto cxt_when;
+ }
+ }
+#endif
+ type = OP_LEAVE;
+ next = NULL;
+ /* Bare blocks (that appear as do { ... } blocks, map { ... } blocks or
+ * constant folded blcoks) don't need to save the op to return to anywhere
+ * since 'last' isn't supposed to work inside them. So we climb higher in
+ * the context stack until we reach a context that has a return op (i.e. a
+ * sub, an eval, a format or a real loop), recording how many blocks we
+ * crossed. Then we follow the op_next chain until we get to the leave op
+ * that closes the original block, which we are assured to reach since
+ * everything is static (the blocks we have crossed cannot be evals or
+ * subroutine calls). */
+ for (i = cxix + 1; i <= cur; ++i) {
+ PERL_CONTEXT *cx2 = cxstack + i;
+ switch (CxTYPE(cx2)) {
+ case CXt_BLOCK:
+ ++n;
+ break;
+ case CXt_SUB:
+ case CXt_FORMAT:
+ o = SU_RETOP_SUB(cx2);
+ break;
+ case CXt_EVAL:
+ o = SU_RETOP_EVAL(cx2);
+ break;
+#if SU_HAS_PERL(5, 11, 0)
+ case CXt_LOOP_FOR:
+ case CXt_LOOP_PLAIN:
+ case CXt_LOOP_LAZYSV:
+ case CXt_LOOP_LAZYIV:
+#else
+ case CXt_LOOP:
+#endif
+ o = SU_RETOP_LOOP(cx2);
+ break;
+ }
+ if (o)
+ break;
+ }
+ if (!o)
+ o = PL_op;
+ while (n && o) {
+ /* We may find other enter/leave blocks on our way to the matching leave.
+ * Make sure the depth is incremented/decremented appropriately. */
+ if (o->op_type == OP_ENTER) {
+ ++n;
+ } else if (o->op_type == OP_LEAVE) {
+ --n;
+ if (!n) {
+ next = o->op_next;
+ break;
+ }
+ }
+ o = o->op_next;
+ }
+ break;
+ }
+ case CXt_SUB:
+ case CXt_FORMAT:
+ type = OP_LEAVESUB;
+ next = SU_RETOP_SUB(cx);
+ break;
+ case CXt_EVAL:
+ type = CxTRYBLOCK(cx) ? OP_LEAVETRY : OP_LEAVEEVAL;
+ next = SU_RETOP_EVAL(cx);
+ break;
+#if SU_HAS_PERL(5, 11, 0)
+ case CXt_LOOP_FOR:
+ case CXt_LOOP_PLAIN:
+ case CXt_LOOP_LAZYSV:
+ case CXt_LOOP_LAZYIV:
+#else
+ case CXt_LOOP:
+#endif
+ type = OP_LEAVELOOP;
+ next = SU_RETOP_LOOP(cx);
+ break;
+#if SU_HAS_PERL(5, 10, 0)
+ case CXt_GIVEN:
+cxt_given:
+ type = OP_LEAVEGIVEN;
+ next = SU_RETOP_GIVEN(cx);
+ break;
+ case CXt_WHEN:
+cxt_when:
+#if SU_HAS_PERL(5, 15, 1)
+ type = OP_LEAVEWHEN;
+#else
+ type = OP_BREAK;
+ flags |= OPf_SPECIAL;
+#endif
+ next = NULL;
+ break;
+#endif
+ case CXt_SUBST:
+ croak("%s() can't target a substitution context", which);
+ break;
+ default:
+ croak("%s() doesn't know how to leave a %s context",
+ which, SU_CXNAME(cxstack + cxix));
+ break;
+ }
+
+ PL_stack_sp = MY_CXT.yield_storage.savesp;
+#if SU_HAS_PERL(5, 19, 4)
+ {
+ I32 i;
+ SV **sp = PL_stack_sp;
+ for (i = -items + 1; i <= 0; ++i)
+ if (!SvTEMP(sp[i]))
+ sv_2mortal(SvREFCNT_inc(sp[i]));
+ }
+#endif
+
+ if (cxstack_ix > cxix)
+ dounwind(cxix);
+
+ /* Copy the arguments passed to yield() where the leave op expects to find
+ * them. */
+ if (items)
+ Move(PL_stack_sp - items + 1, PL_stack_base + cx->blk_oldsp + 1, items, SV *);
+ PL_stack_sp = PL_stack_base + cx->blk_oldsp + items;
+
+ flags |= OP_GIMME_REVERSE(cx->blk_gimme);
+
+ MY_CXT.yield_storage.leave_op.op_type = type;
+ MY_CXT.yield_storage.leave_op.op_ppaddr = PL_ppaddr[type];
+ MY_CXT.yield_storage.leave_op.op_flags = flags;
+ MY_CXT.yield_storage.leave_op.op_next = next;
+
+ PL_op = (OP *) &(MY_CXT.yield_storage.leave_op);
+ PL_op = PL_op->op_ppaddr(aTHX);
+
+ MY_CXT.yield_storage.proxy_op.op_next = PL_op;
+ PL_op = &(MY_CXT.yield_storage.proxy_op);
+}
+
+/* --- Uplevel ------------------------------------------------------------- */
+
+#define SU_UPLEVEL_SAVE(f, t) STMT_START { sud->old_##f = PL_##f; PL_##f = (t); } STMT_END
+#define SU_UPLEVEL_RESTORE(f) STMT_START { PL_##f = sud->old_##f; } STMT_END
+
+static su_uplevel_ud *su_uplevel_storage_new(pTHX_ I32 cxix) {
+#define su_uplevel_storage_new(I) su_uplevel_storage_new(aTHX_ (I))
+ su_uplevel_ud *sud;
+ UV depth;
+ dMY_CXT;
+
+ sud = MY_CXT.uplevel_storage.root;
+ if (sud) {
+ MY_CXT.uplevel_storage.root = sud->next;
+ MY_CXT.uplevel_storage.count--;
+ } else {
+ sud = su_uplevel_ud_new();
+ }
+
+ sud->next = MY_CXT.uplevel_storage.top;
+ MY_CXT.uplevel_storage.top = sud;
+
+ depth = su_uid_depth(cxix);
+ su_uid_storage_dup(&sud->tmp_uid_storage, &MY_CXT.uid_storage, depth);
+ sud->old_uid_storage = MY_CXT.uid_storage;
+ MY_CXT.uid_storage = sud->tmp_uid_storage;
+
+ return sud;
+}
+
+#if SU_HAS_PERL(5, 13, 7)
+
+static void su_uplevel_storage_delete(pTHX_ su_uplevel_ud *sud) {
+#define su_uplevel_storage_delete(S) su_uplevel_storage_delete(aTHX_ (S))
+ dMY_CXT;
+
+ sud->tmp_uid_storage = MY_CXT.uid_storage;
+ MY_CXT.uid_storage = sud->old_uid_storage;
+ {
+ su_uid **map;
+ UV i, alloc;
+ map = sud->tmp_uid_storage.map;
+ alloc = sud->tmp_uid_storage.alloc;
+ for (i = 0; i < alloc; ++i) {
+ if (map[i])
+ map[i]->flags &= SU_UID_ACTIVE;
+ }
+ }
+ MY_CXT.uplevel_storage.top = sud->next;
+
+ if (MY_CXT.uplevel_storage.count >= SU_UPLEVEL_STORAGE_SIZE) {
+ su_uplevel_ud_delete(sud);
+ } else {
+ sud->next = MY_CXT.uplevel_storage.root;
+ MY_CXT.uplevel_storage.root = sud;
+ MY_CXT.uplevel_storage.count++;
+ }
+}
+
+#endif
+
+static int su_uplevel_goto_static(const OP *o) {
+ for (; o; o = OpSIBLING(o)) {
+ /* goto ops are unops with kids. */
+ if (!(o->op_flags & OPf_KIDS))
+ continue;
+
+ switch (o->op_type) {
+ case OP_LEAVEEVAL:
+ case OP_LEAVETRY:
+ /* Don't care about gotos inside eval, as they are forbidden at run time. */
+ break;
+ case OP_GOTO:
+ return 1;
+ default:
+ if (su_uplevel_goto_static(((const UNOP *) o)->op_first))
+ return 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+#if SU_UPLEVEL_HIJACKS_RUNOPS
+
+static int su_uplevel_goto_runops(pTHX) {
+#define su_uplevel_goto_runops() su_uplevel_goto_runops(aTHX)
+ register OP *op;
+ dVAR;
+
+ op = PL_op;
+ do {
+ if (op->op_type == OP_GOTO) {
+ AV *argarray = NULL;
+ I32 cxix;
+
+ for (cxix = cxstack_ix; cxix >= 0; --cxix) {
+ const PERL_CONTEXT *cx = cxstack + cxix;
+
+ switch (CxTYPE(cx)) {
+ case CXt_SUB:
+ if (CxHASARGS(cx)) {
+ argarray = cx->blk_sub.argarray;
+ goto done;
+ }
+ break;
+ case CXt_EVAL:
+ case CXt_FORMAT:
+ goto done;
+ default:
+ break;
+ }
+ }