X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=Upper.xs;h=b68b08f24fe9d226189e42ad994f841e2c3d15c7;hb=refs%2Ftags%2Fv0.17;hp=bb380fe56e97cf591671543ff52478813c599dc7;hpb=f096bbc131e6ca04b079a22f2e48efff1a6c3ddb;p=perl%2Fmodules%2FScope-Upper.git diff --git a/Upper.xs b/Upper.xs index bb380fe..b68b08f 100644 --- a/Upper.xs +++ b/Upper.xs @@ -3,7 +3,7 @@ #define PERL_NO_GET_CONTEXT #include "EXTERN.h" -#include "perl.h" +#include "perl.h" #include "XSUB.h" #define __PACKAGE__ "Scope::Upper" @@ -195,19 +195,23 @@ typedef struct { void *next; I32 cxix; + bool died; + CV *target; + I32 target_depth; + CV *callback; - bool died; + CV *renamed; PERL_SI *si; PERL_SI *old_curstackinfo; AV *old_mainstack; - I32 old_depth; COP *old_curcop; - bool old_catch; - OP *old_op; + runops_proc_t old_runops; + bool old_catch; + OP *old_op; } su_uplevel_ud; STATIC su_uplevel_ud *su_uplevel_ud_new(pTHX) { @@ -242,6 +246,7 @@ STATIC void su_uplevel_ud_delete(pTHX_ su_uplevel_ud *sud) { } typedef struct { + su_uplevel_ud *top; su_uplevel_ud *root; I32 count; } su_uplevel_storage; @@ -971,6 +976,9 @@ STATIC su_uplevel_ud *su_uplevel_storage_new(pTHX) { sud = su_uplevel_ud_new(); } + sud->next = MY_CXT.uplevel_storage.top; + MY_CXT.uplevel_storage.top = sud; + return sud; } @@ -978,6 +986,8 @@ 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; + MY_CXT.uplevel_storage.top = sud->next; + if (MY_CXT.uplevel_storage.count >= SU_UPLEVEL_STORAGE_SIZE) { su_uplevel_ud_delete(sud); } else { @@ -987,54 +997,132 @@ STATIC void su_uplevel_storage_delete(pTHX_ su_uplevel_ud *sud) { } } -#define SU_HAS_EXT_MAGIC SU_HAS_PERL(5, 8, 0) - -#if SU_HAS_EXT_MAGIC && !SU_HAS_PERL(5, 13, 7) +STATIC int su_uplevel_goto_static(const OP *o) { + for (; o; o = o->op_sibling) { + /* goto ops are unops with kids. */ + if (!(o->op_flags & OPf_KIDS)) + continue; -STATIC int su_uplevel_restore_free(pTHX_ SV *sv, MAGIC *mg) { - su_uplevel_storage_delete((su_uplevel_ud *) mg->mg_ptr); + 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(cUNOPo->op_first)) + return 1; + break; + } + } return 0; } -STATIC MGVTBL su_uplevel_restore_vtbl = { - 0, - 0, - 0, - 0, - su_uplevel_restore_free -}; +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; + } + } + +done: + if (argarray) { + dMY_CXT; + + if (MY_CXT.uplevel_storage.top->cxix == cxix) { + AV *args = GvAV(PL_defgv); + I32 items = AvFILLp(args); + + av_extend(argarray, items); + Copy(AvARRAY(args), AvARRAY(argarray), items + 1, SV *); + AvFILLp(argarray) = items; + } + } + } + + PL_op = op = op->op_ppaddr(aTHX); + +#if !SU_HAS_PERL(5, 13, 0) + PERL_ASYNC_CHECK(); +#endif + } while (op); + + TAINT_NOT; -#endif /* SU_HAS_EXT_MAGIC && !SU_HAS_PERL(5, 13, 7) */ + return 0; +} + +#define su_at_underscore(C) AvARRAY(AvARRAY(CvPADLIST(C))[CvDEPTH(C)])[0] STATIC void su_uplevel_restore(pTHX_ void *sus_) { su_uplevel_ud *sud = sus_; - const PERL_CONTEXT *sub_cx; PERL_SI *cur = sud->old_curstackinfo; PERL_SI *si = sud->si; - sub_cx = cxstack + sud->cxix; + if (PL_runops == su_uplevel_goto_runops) + PL_runops = sud->old_runops; + + if (sud->callback) { + PERL_CONTEXT *cx = cxstack + sud->cxix; + AV *argarray = MUTABLE_AV(su_at_underscore(sud->callback)); + + /* We have to fix the pad entry for @_ in the original callback because it + * may have been reified. */ + if (AvREAL(argarray)) { + const I32 fill = AvFILLp(argarray); + SvREFCNT_dec(argarray); + argarray = newAV(); + AvREAL_off(argarray); + AvREIFY_on(argarray); + av_extend(argarray, fill); + su_at_underscore(sud->callback) = MUTABLE_SV(argarray); + } else { + CLEAR_ARGARRAY(argarray); + } - /* When we reach this place, POPSUB has already been called (with our fake - * argarray). GvAV(PL_defgv) points to the savearray (that is, what @_ was - * before uplevel). argarray is either the fake AV we created in su_uplevel() - * or some empty replacement POPSUB creates when @_ is reified. In both cases - * we have to destroy it before the context stack is swapped back to its - * original state. */ - SvREFCNT_dec(sub_cx->blk_sub.argarray); + /* If the old cv member is our renamed CV, it means that this place has been + * reached without a goto() happening, and the old argarray member is + * actually our fake argarray. Destroy it properly in that case. */ + if (cx->blk_sub.cv == sud->renamed) { + SvREFCNT_dec(cx->blk_sub.argarray); + cx->blk_sub.argarray = argarray; + } - /* PUSHSUB was exerted with the original callback, but after calling - * pp_entersub() we hijacked the blk_sub.cv member of the fresh sub context - * with the renamed CV. Thus POPSUB and LEAVESUB applied to this CV, not the - * original. Repair this imbalance right now. */ - if (!(CvDEPTH(sud->callback) = sub_cx->blk_sub.olddepth)) - LEAVESUB(sud->callback); + CvDEPTH(sud->callback)--; + SvREFCNT_dec(sud->callback); + } - /* Free the renamed cv. */ - { - CV *renamed_cv = sub_cx->blk_sub.cv; - CvDEPTH(renamed_cv) = 0; - SvREFCNT_dec(renamed_cv); + /* Free the renamed CV. We must do it ourselves so that we can force the + * depth to be 0, or perl would complain about it being "still in use". + * But we *know* that it cannot be so. */ + if (sud->renamed) { + CvDEPTH(sud->renamed) = 0; + CvPADLIST(sud->renamed) = NULL; + SvREFCNT_dec(sud->renamed); } CATCH_SET(sud->old_catch); @@ -1049,7 +1137,7 @@ STATIC void su_uplevel_restore(pTHX_ void *sus_) { SU_UPLEVEL_RESTORE(curstackinfo); if (sud->died) { - CV *target_cv = sud->target; + CV *target = sud->target; I32 levels = 0, i; /* When we die, the depth of the target CV is not updated because of the @@ -1060,7 +1148,7 @@ STATIC void su_uplevel_restore(pTHX_ void *sus_) { register const PERL_CONTEXT *cx = cxstack + i; if (CxTYPE(cx) == CXt_SUB) { - if (cx->blk_sub.cv == target_cv) + if (cx->blk_sub.cv == target) ++levels; } } @@ -1077,7 +1165,7 @@ STATIC void su_uplevel_restore(pTHX_ void *sus_) { switch (CxTYPE(cx)) { case CXt_SUB: - if (cx->blk_sub.cv == target_cv) + if (cx->blk_sub.cv == target) ++levels; break; case CXt_EVAL: @@ -1089,7 +1177,7 @@ STATIC void su_uplevel_restore(pTHX_ void *sus_) { } found_it: - CvDEPTH(target_cv) = sud->old_depth - levels; + CvDEPTH(target) = sud->target_depth - levels; PL_curstackinfo->si_cxix = i - 1; #if !SU_HAS_PERL(5, 13, 1) @@ -1130,20 +1218,12 @@ found_it: /* This issue has been fixed in perl with commit 8f89e5a9, which was made * public in perl 5.13.7. */ su_uplevel_storage_delete(sud); -#elif SU_HAS_EXT_MAGIC - /* If 'ext' magic is available, we work around this by attaching the state - * data to a scalar that will be freed "soon". */ - { - SV *sv = sv_newmortal(); - - sv_magicext(sv, NULL, PERL_MAGIC_ext, &su_uplevel_restore_vtbl, - (const char *) sud, 0); - } #else /* Otherwise, we just enqueue it back in the global storage list. */ { dMY_CXT; + MY_CXT.uplevel_storage.top = sud->next; sud->next = MY_CXT.uplevel_storage.root; MY_CXT.uplevel_storage.root = sud; MY_CXT.uplevel_storage.count++; @@ -1156,12 +1236,6 @@ found_it: STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) { #define su_cv_clone(P, G) su_cv_clone(aTHX_ (P), (G)) dVAR; - AV *protopadlist = CvPADLIST(proto); - const AV *protopadname = (const AV *) *av_fetch(protopadlist, 0, FALSE); - SV **pname = AvARRAY(protopadname); - const I32 fpadlist = AvFILLp(protopadlist); - const I32 fpadname = AvFILLp(protopadname); - AV *padlist, *padname; CV *cv; cv = MUTABLE_CV(newSV_type(SvTYPE(proto))); @@ -1179,16 +1253,29 @@ STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) { CvGV_set(cv, gv); CvSTASH_set(cv, CvSTASH(proto)); + /* Commit 4c74a7df, publicized with perl 5.13.3, began to add backrefs to + * stashes. CvSTASH_set() started to do it as well with commit c68d95645 + * (which was part of perl 5.13.7). */ +#if SU_HAS_PERL(5, 13, 3) && !SU_HAS_PERL(5, 13, 7) + if (CvSTASH(proto)) + Perl_sv_add_backref(aTHX_ CvSTASH(proto), MUTABLE_SV(cv)); +#endif - OP_REFCNT_LOCK; - CvROOT(cv) = OpREFCNT_inc(CvROOT(proto)); - OP_REFCNT_UNLOCK; - CvSTART(cv) = CvSTART(proto); + if (CvISXSUB(proto)) { + CvXSUB(cv) = CvXSUB(proto); + CvXSUBANY(cv) = CvXSUBANY(proto); + } else { + OP_REFCNT_LOCK; + CvROOT(cv) = OpREFCNT_inc(CvROOT(proto)); + OP_REFCNT_UNLOCK; + CvSTART(cv) = CvSTART(proto); + } CvOUTSIDE(cv) = CvOUTSIDE(proto); #ifdef CVf_WEAKOUTSIDE if (!(CvFLAGS(proto) & CVf_WEAKOUTSIDE)) #endif SvREFCNT_inc_simple_void(CvOUTSIDE(cv)); + CvPADLIST(cv) = CvPADLIST(proto); #ifdef CvOUTSIDE_SEQ CvOUTSIDE_SEQ(cv) = CvOUTSIDE_SEQ(proto); #endif @@ -1196,51 +1283,6 @@ STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) { if (SvPOK(proto)) sv_setpvn(MUTABLE_SV(cv), SvPVX_const(proto), SvCUR(proto)); - padlist = newAV(); - AvREAL_off(padlist); - av_fill(padlist, fpadlist); - CvPADLIST(cv) = padlist; - - padname = newAV(); - av_fill(padname, fpadname); - if (fpadname >= 0) { - I32 j; - SV **psvp = AvARRAY(protopadname); - SV **svp = AvARRAY(padname); - - svp[0] = &PL_sv_undef; - for (j = 1; j <= fpadname; ++j) - svp[j] = SvREFCNT_inc(psvp[j]); - } - AvARRAY(padlist)[0] = MUTABLE_SV(padname); - - if (fpadlist >= 1) { - I32 i; - - for (i = 1; i <= fpadlist; ++i) { - AV *protoframe = MUTABLE_AV(AvARRAY(protopadlist)[i]); - AV *frame = newAV(); - SV **psvp = AvARRAY(protoframe); - SV **svp; - I32 j, fframe = AvFILLp(protoframe); - - av_fill(frame, fframe); - svp = AvARRAY(frame); - if (i == 1) { - AV *a0 = newAV(); /* will be @_ */ - AvREAL_off(a0); - AvREIFY_on(a0); - svp[0] = MUTABLE_SV(a0); - } else { - svp[0] = SvREFCNT_inc(psvp[0]); - } - for (j = 1; j <= fframe; ++j) - svp[j] = SvREFCNT_inc(psvp[j]); - - AvARRAY(padlist)[i] = MUTABLE_SV(frame); - } - } - #ifdef CvCONST if (CvCONST(cv)) CvCONST_off(cv); @@ -1249,14 +1291,15 @@ STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) { return cv; } -STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { +STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) { #define su_uplevel(C, I, A) su_uplevel(aTHX_ (C), (I), (A)) su_uplevel_ud *sud; const PERL_CONTEXT *cx = cxstack + cxix; PERL_SI *si; PERL_SI *cur = PL_curstackinfo; SV **old_stack_sp; - CV *target_cv; + CV *target; + CV *renamed; UNOP sub_op; I32 gimme; I32 old_mark, new_mark; @@ -1275,7 +1318,8 @@ STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { sud->cxix = cxix; sud->died = 1; - sud->callback = cv; + sud->callback = NULL; + sud->renamed = NULL; SAVEDESTRUCTOR_X(su_uplevel_restore, sud); si = sud->si; @@ -1312,9 +1356,9 @@ STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { Copy(cur->si_cxstack, si->si_cxstack, cxix, PERL_CONTEXT); SU_POISON(si->si_cxstack + cxix, si->si_cxmax + 1 - cxix, PERL_CONTEXT); - target_cv = cx->blk_sub.cv; - sud->target = (CV *) SvREFCNT_inc(target_cv); - sud->old_depth = CvDEPTH(target_cv); + target = cx->blk_sub.cv; + sud->target = (CV *) SvREFCNT_inc(target); + sud->target_depth = CvDEPTH(target); /* blk_oldcop is essentially needed for caller() and stack traces. It has no * run-time implication, since PL_curcop will be overwritten as soon as we @@ -1333,11 +1377,14 @@ STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { sud->old_mainstack = NULL; PL_curstack = si->si_stack; + renamed = su_cv_clone(callback, CvGV(target)); + sud->renamed = renamed; + PUSHMARK(SP); /* Both SP and old_stack_sp point just before the CV. */ Copy(old_stack_sp + 2, SP + 1, args, SV *); SP += args; - PUSHs((SV *) cv); + PUSHs((SV *) renamed); PUTBACK; Zero(&sub_op, 1, UNOP); @@ -1349,22 +1396,19 @@ STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { SU_UPLEVEL_SAVE(op, (OP *) &sub_op); + sud->old_runops = PL_runops; + sud->old_catch = CATCH_GET; CATCH_SET(TRUE); if ((PL_op = PL_ppaddr[OP_ENTERSUB](aTHX))) { - PERL_CONTEXT *sub_cx; - CV *renamed_cv; + PERL_CONTEXT *sub_cx = cxstack + cxstack_ix; - renamed_cv = su_cv_clone(cv, CvGV(target_cv)); + /* If pp_entersub() returns a non-null OP, it means that the callback is not + * an XSUB. */ - sub_cx = cxstack + cxstack_ix; - sub_cx->blk_sub.cv = renamed_cv; - if (!sub_cx->blk_sub.olddepth) { - SvREFCNT_inc_simple_void(renamed_cv); - SvREFCNT_inc_simple_void(renamed_cv); - SAVEFREESV(renamed_cv); - } + sud->callback = MUTABLE_CV(SvREFCNT_inc(callback)); + CvDEPTH(callback)++; if (CxHASARGS(cx) && cx->blk_sub.argarray) { /* The call to pp_entersub() has saved the current @_ (in XS terms, @@ -1374,23 +1418,33 @@ STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { * argarray with an unreal copy of the original @_. */ AV *av = newAV(); AvREAL_off(av); + AvREIFY_on(av); av_extend(av, AvMAX(cx->blk_sub.argarray)); AvFILLp(av) = AvFILLp(cx->blk_sub.argarray); Copy(AvARRAY(cx->blk_sub.argarray), AvARRAY(av), AvFILLp(av) + 1, SV *); - cxstack[cxix].blk_sub.argarray = av; + sub_cx->blk_sub.argarray = av; } else { - SvREFCNT_inc_simple_void(cxstack[cxix].blk_sub.argarray); + SvREFCNT_inc_simple_void(sub_cx->blk_sub.argarray); } - CALLRUNOPS(aTHX); + if (su_uplevel_goto_static(CvROOT(renamed))) { + if (PL_runops != Perl_runops_standard) { + if (PL_runops == Perl_runops_debug) { + if (PL_debug) + croak("uplevel() can't execute code that calls goto when debugging flags are set"); + } else if (PL_runops != su_uplevel_goto_runops) + croak("uplevel() can't execute code that calls goto with a custom runloop"); + } + + PL_runops = su_uplevel_goto_runops; + } - ret = PL_stack_sp - (PL_stack_base + new_mark); + CALLRUNOPS(aTHX); } sud->died = 0; - SPAGAIN; - + ret = PL_stack_sp - (PL_stack_base + new_mark); if (ret > 0) { AV *old_stack = sud->old_curstackinfo->si_stack; @@ -1405,8 +1459,6 @@ STATIC I32 su_uplevel(pTHX_ CV *cv, I32 cxix, I32 args) { AvFILLp(old_stack) += ret; } - PUTBACK; - LEAVE; return ret; @@ -1446,6 +1498,7 @@ STATIC void su_setup(pTHX) { MY_CXT.unwind_storage.proxy_op.op_type = OP_STUB; MY_CXT.unwind_storage.proxy_op.op_ppaddr = NULL; + MY_CXT.uplevel_storage.top = NULL; MY_CXT.uplevel_storage.root = NULL; MY_CXT.uplevel_storage.count = 0; @@ -1584,6 +1637,7 @@ PROTOTYPE: DISABLE PPCODE: { MY_CXT_CLONE; + MY_CXT.uplevel_storage.top = NULL; MY_CXT.uplevel_storage.root = NULL; MY_CXT.uplevel_storage.count = 0; }