X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=Upper.xs;h=b68b08f24fe9d226189e42ad994f841e2c3d15c7;hb=refs%2Ftags%2Fv0.17;hp=fadbe3d8f7d2733687c11372e4c86c165d07ccd2;hpb=0c640ded3d3d3939e6fb99c3a94c6bf6e54a360a;p=perl%2Fmodules%2FScope-Upper.git diff --git a/Upper.xs b/Upper.xs index fadbe3d..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" @@ -201,19 +201,17 @@ typedef struct { I32 target_depth; CV *callback; - I32 callback_depth; CV *renamed; - AV *fake_argarray; - PERL_SI *si; PERL_SI *old_curstackinfo; AV *old_mainstack; 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) { @@ -248,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; @@ -977,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; } @@ -984,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 { @@ -993,49 +997,131 @@ STATIC void su_uplevel_storage_delete(pTHX_ su_uplevel_ud *sud) { } } -#define SU_HAS_EXT_MAGIC SU_HAS_PERL(5, 8, 0) +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; -#if SU_HAS_EXT_MAGIC && !SU_HAS_PERL(5, 13, 7) - -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; -#endif /* SU_HAS_EXT_MAGIC && !SU_HAS_PERL(5, 13, 7) */ + 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; + + 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_; PERL_SI *cur = sud->old_curstackinfo; PERL_SI *si = sud->si; - /* 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(sud->fake_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) = sud->callback_depth)) - LEAVESUB(sud->callback); - - /* Free the renamed cv. */ + 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); + } + + /* 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; + } + + CvDEPTH(sud->callback)--; + SvREFCNT_dec(sud->callback); + } + + /* 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; + CvDEPTH(sud->renamed) = 0; + CvPADLIST(sud->renamed) = NULL; SvREFCNT_dec(sud->renamed); } @@ -1132,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++; @@ -1158,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))); @@ -1189,15 +1261,21 @@ STATIC CV *su_cv_clone(pTHX_ CV *proto, GV *gv) { 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 @@ -1205,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); @@ -1266,6 +1299,7 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) { PERL_SI *cur = PL_curstackinfo; SV **old_stack_sp; CV *target; + CV *renamed; UNOP sub_op; I32 gimme; I32 old_mark, new_mark; @@ -1282,11 +1316,10 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) { sud = su_uplevel_storage_new(); - sud->cxix = cxix; - sud->died = 1; - sud->callback = callback; - sud->renamed = NULL; - sud->fake_argarray = NULL; + sud->cxix = cxix; + sud->died = 1; + sud->callback = NULL; + sud->renamed = NULL; SAVEDESTRUCTOR_X(su_uplevel_restore, sud); si = sud->si; @@ -1344,11 +1377,14 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, 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 *) callback); + PUSHs((SV *) renamed); PUTBACK; Zero(&sub_op, 1, UNOP); @@ -1360,24 +1396,19 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, 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 = cxstack + cxstack_ix; - CV *renamed; - - sud->callback_depth = sub_cx->blk_sub.olddepth; - renamed = su_cv_clone(callback, CvGV(target)); - sud->renamed = renamed; + /* If pp_entersub() returns a non-null OP, it means that the callback is not + * an XSUB. */ - sub_cx->blk_sub.cv = renamed; - if (!sub_cx->blk_sub.olddepth) { - SvREFCNT_inc_simple_void(renamed); - SvREFCNT_inc_simple_void(renamed); - SAVEFREESV(renamed); - } + 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, @@ -1387,6 +1418,7 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, 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 *); @@ -1394,17 +1426,25 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) { } else { SvREFCNT_inc_simple_void(sub_cx->blk_sub.argarray); } - sud->fake_argarray = 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; @@ -1419,8 +1459,6 @@ STATIC I32 su_uplevel(pTHX_ CV *callback, I32 cxix, I32 args) { AvFILLp(old_stack) += ret; } - PUTBACK; - LEAVE; return ret; @@ -1460,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; @@ -1598,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; }