X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=Upper.xs;h=3de19a9b6e5c04726e706fd1eda47d405c9a15a3;hb=51c9cbffce75ccfe84b3ba9627ae0d697b0acf29;hp=f90f31917886f4c1eef471e1da9f72e3b5bbd3b5;hpb=7c2b31131b09869b8021b94eb18d14b0df4b356c;p=perl%2Fmodules%2FScope-Upper.git diff --git a/Upper.xs b/Upper.xs index f90f319..3de19a9 100644 --- a/Upper.xs +++ b/Upper.xs @@ -286,7 +286,6 @@ STATIC void su_uid_storage_dup(pTHX_ su_uid_storage *new_cxt, const su_uid_stora if (old_map) { su_uid **new_map = new_cxt->map; STRLEN old_used = old_cxt->used; - STRLEN old_alloc = old_cxt->alloc; STRLEN new_used, new_alloc; STRLEN i; @@ -338,6 +337,16 @@ typedef struct { OP proxy_op; } su_unwind_storage; +/* --- yield() global storage ---------------------------------------------- */ + +typedef struct { + I32 cxix; + I32 items; + SV **savesp; + UNOP leave_op; + OP proxy_op; +} su_yield_storage; + /* --- uplevel() data tokens and global storage ---------------------------- */ #define SU_UPLEVEL_HIJACKS_RUNOPS SU_HAS_PERL(5, 8, 0) @@ -435,6 +444,7 @@ typedef struct { typedef struct { char *stack_placeholder; su_unwind_storage unwind_storage; + su_yield_storage yield_storage; su_uplevel_storage uplevel_storage; su_uid_storage uid_storage; } my_cxt_t; @@ -907,12 +917,10 @@ done: /* --- Pop a context back -------------------------------------------------- */ -#if SU_DEBUG -# ifdef DEBUGGING -# define SU_CXNAME(C) PL_block_type[CxTYPE(C)] -# else -# define SU_CXNAME(C) "XXX" -# endif +#if SU_DEBUG && defined(DEBUGGING) +# define SU_CXNAME(C) PL_block_type[CxTYPE(C)] +#else +# define SU_CXNAME(C) "XXX" #endif STATIC void su_pop(pTHX_ void *ud) { @@ -1063,23 +1071,17 @@ STATIC I32 su_init(pTHX_ void *ud, I32 cxix, I32 size) { STATIC void su_unwind(pTHX_ void *ud_) { dMY_CXT; - I32 cxix = MY_CXT.unwind_storage.cxix; - I32 items = MY_CXT.unwind_storage.items - 1; - SV **savesp = MY_CXT.unwind_storage.savesp; + I32 cxix = MY_CXT.unwind_storage.cxix; + I32 items = MY_CXT.unwind_storage.items; I32 mark; PERL_UNUSED_VAR(ud_); - if (savesp) - PL_stack_sp = savesp; + PL_stack_sp = MY_CXT.unwind_storage.savesp; if (cxstack_ix > cxix) dounwind(cxix); - /* Hide the level */ - if (items >= 0) - PL_stack_sp--; - mark = PL_markstack[cxstack[cxix].blk_oldmarksp]; *PL_markstack_ptr = PL_stack_sp - PL_stack_base - items; @@ -1101,6 +1103,180 @@ STATIC void su_unwind(pTHX_ void *ud_) { 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 type = CxTYPE(prev); + if ((type == CXt_GIVEN || type == CXt_WHEN) + && (prev->blk_oldcop == cx->blk_oldcop)) { + cxix--; + cx = prev; + if (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 (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 @@ -1172,7 +1348,7 @@ STATIC int su_uplevel_goto_static(const OP *o) { case OP_GOTO: return 1; default: - if (su_uplevel_goto_static(cUNOPo->op_first)) + if (su_uplevel_goto_static(((const UNOP *) o)->op_first)) return 1; break; } @@ -1801,6 +1977,9 @@ STATIC I32 su_context_skip_db(pTHX_ I32 cxix) { PERL_CONTEXT *cx = cxstack + i; switch (CxTYPE(cx)) { +#if SU_HAS_PERL(5, 17, 1) + case CXt_LOOP_PLAIN: +#endif case CXt_BLOCK: if (cx->blk_oldcop && CopSTASH(cx->blk_oldcop) == GvSTASH(PL_DBgv)) continue; @@ -1821,8 +2000,9 @@ STATIC I32 su_context_skip_db(pTHX_ I32 cxix) { return cxix; } -STATIC I32 su_context_up(pTHX_ I32 cxix) { -#define su_context_up(C) su_context_up(aTHX_ (C)) + +STATIC I32 su_context_normalize_up(pTHX_ I32 cxix) { +#define su_context_normalize_up(C) su_context_normalize_up(aTHX_ (C)) PERL_CONTEXT *cx; if (cxix <= 0) @@ -1844,28 +2024,57 @@ STATIC I32 su_context_up(pTHX_ I32 cxix) { case CXt_LOOP: #endif if (cx->blk_oldcop == prev->blk_oldcop) - cxix -= 2; - else - --cxix; + return cxix - 1; break; case CXt_SUBST: if (cx->blk_oldcop && cx->blk_oldcop->op_sibling && cx->blk_oldcop->op_sibling->op_type == OP_SUBST) - cxix -= 2; - else - --cxix; + return cxix - 1; break; - default: - --cxix; + } + } + + return cxix; +} + +STATIC I32 su_context_normalize_down(pTHX_ I32 cxix) { +#define su_context_normalize_down(C) su_context_normalize_down(aTHX_ (C)) + PERL_CONTEXT *next; + + if (cxix >= cxstack_ix) + return cxstack_ix; + + next = cxstack + cxix + 1; + if (CxTYPE(next) == CXt_BLOCK) { + PERL_CONTEXT *cx = next - 1; + + switch (CxTYPE(cx)) { +#if SU_HAS_PERL(5, 10, 0) + case CXt_GIVEN: + case CXt_WHEN: +#endif +#if SU_HAS_PERL(5, 11, 0) + /* That's the only subcategory that can cause an extra BLOCK context */ + case CXt_LOOP_PLAIN: +#else + case CXt_LOOP: +#endif + if (cx->blk_oldcop == next->blk_oldcop) + return cxix + 1; + break; + case CXt_SUBST: + if (next->blk_oldcop && next->blk_oldcop->op_sibling + && next->blk_oldcop->op_sibling->op_type == OP_SUBST) + return cxix + 1; break; } - } else { - --cxix; } return cxix; } +#define su_context_here() su_context_normalize_up(su_context_skip_db(cxstack_ix)) + /* --- Interpreter setup/teardown ------------------------------------------ */ STATIC void su_teardown(pTHX_ void *param) { @@ -1909,6 +2118,14 @@ STATIC void su_setup(pTHX) { MY_CXT.unwind_storage.proxy_op.op_type = OP_STUB; MY_CXT.unwind_storage.proxy_op.op_ppaddr = NULL; + Zero(&(MY_CXT.yield_storage.leave_op), 1, UNOP); + MY_CXT.yield_storage.leave_op.op_type = OP_STUB; + MY_CXT.yield_storage.leave_op.op_ppaddr = NULL; + + Zero(&(MY_CXT.yield_storage.proxy_op), 1, OP); + MY_CXT.yield_storage.proxy_op.op_type = OP_STUB; + MY_CXT.yield_storage.proxy_op.op_ppaddr = NULL; + MY_CXT.uplevel_storage.top = NULL; MY_CXT.uplevel_storage.root = NULL; MY_CXT.uplevel_storage.count = 0; @@ -1924,21 +2141,21 @@ STATIC void su_setup(pTHX) { /* --- XS ------------------------------------------------------------------ */ -#define SU_GET_CONTEXT(A, B) \ - STMT_START { \ - if (items > A) { \ - SV *csv = ST(B); \ - if (!SvOK(csv)) \ - goto default_cx; \ - cxix = SvIV(csv); \ - if (cxix < 0) \ - cxix = 0; \ - else if (cxix > cxstack_ix) \ - cxix = cxstack_ix; \ - } else { \ -default_cx: \ - cxix = cxstack_ix; \ - } \ +#define SU_GET_CONTEXT(A, B, D) \ + STMT_START { \ + if (items > A) { \ + SV *csv = ST(B); \ + if (!SvOK(csv)) \ + goto default_cx; \ + cxix = SvIV(csv); \ + if (cxix < 0) \ + cxix = 0; \ + else if (cxix > cxstack_ix) \ + goto default_cx; \ + } else { \ +default_cx: \ + cxix = (D); \ + } \ } STMT_END #define SU_GET_LEVEL(A, B) \ @@ -1968,8 +2185,7 @@ XS(XS_Scope__Upper_unwind) { PERL_UNUSED_VAR(cv); /* -W */ PERL_UNUSED_VAR(ax); /* -Wall */ - SU_GET_CONTEXT(0, items - 1); - cxix = su_context_skip_db(cxix); + SU_GET_CONTEXT(0, items - 1, cxstack_ix); do { PERL_CONTEXT *cx = cxstack + cxix; switch (CxTYPE(cx)) { @@ -1978,17 +2194,18 @@ XS(XS_Scope__Upper_unwind) { continue; case CXt_EVAL: case CXt_FORMAT: - MY_CXT.unwind_storage.cxix = cxix; - MY_CXT.unwind_storage.items = items; + MY_CXT.unwind_storage.cxix = cxix; + MY_CXT.unwind_storage.items = items; + MY_CXT.unwind_storage.savesp = PL_stack_sp; + if (items > 0) { + MY_CXT.unwind_storage.items--; + MY_CXT.unwind_storage.savesp--; + } /* pp_entersub will want to sanitize the stack after returning from there - * Screw that, we're insane */ - if (GIMME_V == G_SCALAR) { - MY_CXT.unwind_storage.savesp = PL_stack_sp; - /* dXSARGS calls POPMARK, so we need to match PL_markstack_ptr[1] */ + * Screw that, we're insane! + * dXSARGS calls POPMARK, so we need to match PL_markstack_ptr[1] */ + if (GIMME_V == G_SCALAR) PL_stack_sp = PL_stack_base + PL_markstack_ptr[1] + 1; - } else { - MY_CXT.unwind_storage.savesp = NULL; - } SAVEDESTRUCTOR_X(su_unwind, NULL); return; default: @@ -1998,6 +2215,63 @@ XS(XS_Scope__Upper_unwind) { croak("Can't return outside a subroutine"); } +STATIC const char su_yield_name[] = "yield"; + +XS(XS_Scope__Upper_yield); /* prototype to pass -Wmissing-prototypes */ + +XS(XS_Scope__Upper_yield) { +#ifdef dVAR + dVAR; dXSARGS; +#else + dXSARGS; +#endif + dMY_CXT; + I32 cxix; + + PERL_UNUSED_VAR(cv); /* -W */ + PERL_UNUSED_VAR(ax); /* -Wall */ + + SU_GET_CONTEXT(0, items - 1, su_context_here()); + MY_CXT.yield_storage.cxix = cxix; + MY_CXT.yield_storage.items = items; + MY_CXT.yield_storage.savesp = PL_stack_sp; + if (items > 0) { + MY_CXT.yield_storage.items--; + MY_CXT.yield_storage.savesp--; + } + /* See XS_Scope__Upper_unwind */ + if (GIMME_V == G_SCALAR) + PL_stack_sp = PL_stack_base + PL_markstack_ptr[1] + 1; + SAVEDESTRUCTOR_X(su_yield, su_yield_name); + return; +} + +STATIC const char su_leave_name[] = "leave"; + +XS(XS_Scope__Upper_leave); /* prototype to pass -Wmissing-prototypes */ + +XS(XS_Scope__Upper_leave) { +#ifdef dVAR + dVAR; dXSARGS; +#else + dXSARGS; +#endif + dMY_CXT; + I32 cxix; + + PERL_UNUSED_VAR(cv); /* -W */ + PERL_UNUSED_VAR(ax); /* -Wall */ + + MY_CXT.yield_storage.cxix = su_context_here(); + MY_CXT.yield_storage.items = items; + MY_CXT.yield_storage.savesp = PL_stack_sp; + /* See XS_Scope__Upper_unwind */ + if (GIMME_V == G_SCALAR) + PL_stack_sp = PL_stack_base + PL_markstack_ptr[1] + 1; + SAVEDESTRUCTOR_X(su_yield, su_leave_name); + return; +} + MODULE = Scope::Upper PACKAGE = Scope::Upper PROTOTYPES: ENABLE @@ -2016,6 +2290,8 @@ BOOT: newCONSTSUB(stash, "SU_THREADSAFE", newSVuv(SU_THREADSAFE)); newXSproto("Scope::Upper::unwind", XS_Scope__Upper_unwind, file, NULL); + newXSproto("Scope::Upper::yield", XS_Scope__Upper_yield, file, NULL); + newXSproto("Scope::Upper::leave", XS_Scope__Upper_leave, file, NULL); su_setup(); } @@ -2052,7 +2328,7 @@ PROTOTYPE: PREINIT: I32 cxix; PPCODE: - cxix = su_context_skip_db(cxstack_ix); + cxix = su_context_here(); EXTEND(SP, 1); mPUSHi(cxix); XSRETURN(1); @@ -2063,10 +2339,12 @@ PROTOTYPE: ;$ PREINIT: I32 cxix; PPCODE: - SU_GET_CONTEXT(0, 0); - cxix = su_context_skip_db(cxix); - cxix = su_context_up(cxix); - cxix = su_context_skip_db(cxix); + SU_GET_CONTEXT(0, 0, su_context_here()); + if (cxix > 0) { + --cxix; + cxix = su_context_skip_db(cxix); + cxix = su_context_normalize_up(cxix); + } EXTEND(SP, 1); mPUSHi(cxix); XSRETURN(1); @@ -2077,7 +2355,7 @@ PROTOTYPE: ;$ PREINIT: I32 cxix; PPCODE: - SU_GET_CONTEXT(0, 0); + SU_GET_CONTEXT(0, 0, cxstack_ix); EXTEND(SP, 1); for (; cxix >= 0; --cxix) { PERL_CONTEXT *cx = cxstack + cxix; @@ -2099,7 +2377,7 @@ PROTOTYPE: ;$ PREINIT: I32 cxix; PPCODE: - SU_GET_CONTEXT(0, 0); + SU_GET_CONTEXT(0, 0, cxstack_ix); EXTEND(SP, 1); for (; cxix >= 0; --cxix) { PERL_CONTEXT *cx = cxstack + cxix; @@ -2120,10 +2398,13 @@ PREINIT: I32 cxix, level; PPCODE: SU_GET_LEVEL(0, 0); - cxix = su_context_skip_db(cxstack_ix); + cxix = su_context_here(); while (--level >= 0) { - cxix = su_context_up(cxix); + if (cxix <= 0) + break; + --cxix; cxix = su_context_skip_db(cxix); + cxix = su_context_normalize_up(cxix); } EXTEND(SP, 1); mPUSHi(cxix); @@ -2160,7 +2441,7 @@ PROTOTYPE: ;$ PREINIT: I32 cxix; PPCODE: - SU_GET_CONTEXT(0, 0); + SU_GET_CONTEXT(0, 0, cxstack_ix); EXTEND(SP, 1); while (cxix > 0) { PERL_CONTEXT *cx = cxstack + cxix--; @@ -2189,8 +2470,8 @@ PREINIT: I32 cxix; su_ud_reap *ud; CODE: - SU_GET_CONTEXT(1, 1); - cxix = su_context_skip_db(cxix); + SU_GET_CONTEXT(1, 1, su_context_skip_db(cxstack_ix)); + cxix = su_context_normalize_down(cxix); Newx(ud, 1, su_ud_reap); SU_UD_ORIGIN(ud) = NULL; SU_UD_HANDLER(ud) = su_reap; @@ -2205,8 +2486,8 @@ PREINIT: I32 size; su_ud_localize *ud; CODE: - SU_GET_CONTEXT(2, 2); - cxix = su_context_skip_db(cxix); + SU_GET_CONTEXT(2, 2, su_context_skip_db(cxstack_ix)); + cxix = su_context_normalize_down(cxix); Newx(ud, 1, su_ud_localize); SU_UD_ORIGIN(ud) = NULL; SU_UD_HANDLER(ud) = su_localize; @@ -2223,9 +2504,9 @@ PREINIT: CODE: if (SvTYPE(sv) >= SVt_PVGV) croak("Can't infer the element localization type from a glob and the value"); - SU_GET_CONTEXT(3, 3); + SU_GET_CONTEXT(3, 3, su_context_skip_db(cxstack_ix)); + cxix = su_context_normalize_down(cxix); Newx(ud, 1, su_ud_localize); - cxix = su_context_skip_db(cxix); SU_UD_ORIGIN(ud) = NULL; SU_UD_HANDLER(ud) = su_localize; size = su_ud_localize_init(ud, sv, val, elem); @@ -2243,8 +2524,8 @@ PREINIT: I32 size; su_ud_localize *ud; CODE: - SU_GET_CONTEXT(2, 2); - cxix = su_context_skip_db(cxix); + SU_GET_CONTEXT(2, 2, su_context_skip_db(cxstack_ix)); + cxix = su_context_normalize_down(cxix); Newx(ud, 1, su_ud_localize); SU_UD_ORIGIN(ud) = NULL; SU_UD_HANDLER(ud) = su_localize; @@ -2261,7 +2542,7 @@ PPCODE: code = SvRV(code); if (SvTYPE(code) < SVt_PVCV) croak("First argument to uplevel must be a code reference"); - SU_GET_CONTEXT(1, items - 1); + SU_GET_CONTEXT(1, items - 1, cxstack_ix); do { PERL_CONTEXT *cx = cxstack + cxix; switch (CxTYPE(cx)) { @@ -2292,9 +2573,8 @@ PREINIT: I32 cxix; SV *uid; PPCODE: - SU_GET_CONTEXT(0, 0); - cxix = su_context_skip_db(cxix); - uid = su_uid_get(cxix); + SU_GET_CONTEXT(0, 0, su_context_here()); + uid = su_uid_get(cxix); EXTEND(SP, 1); PUSHs(uid); XSRETURN(1);