X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2FVariable-Magic.git;a=blobdiff_plain;f=Magic.xs;fp=Magic.xs;h=ba59f548791c51eb6c6778eb28fcadfeec60cb19;hp=116eb8a728a23bd241e7c3d1086c55b94359fe55;hb=92d785f98afb5bd5707e622ba9cc68818b8f0b33;hpb=ecc16f5aeded896b3ab1f0019fc489bb5e15bc08 diff --git a/Magic.xs b/Magic.xs index 116eb8a..ba59f54 100644 --- a/Magic.xs +++ b/Magic.xs @@ -155,6 +155,10 @@ static OP *vmg_trampoline_bump(pTHX_ vmg_trampoline *t, SV *sv, OP *o) { # define SvREFCNT_inc_simple_void(sv) ((void) SvREFCNT_inc(sv)) #endif +#ifndef SvREFCNT_dec_NN +# define SvREFCNT_dec_NN(sv) ((void) SvREFCNT_dec(sv)) +#endif + #ifndef mPUSHu # define mPUSHu(U) PUSHs(sv_2mortal(newSVuv(U))) #endif @@ -1416,6 +1420,7 @@ static MGVTBL vmg_propagate_errsv_vtbl = { typedef struct { SV *sv; + SV *rsv; /* The ref to the sv currently being freed, pushed on the stack */ int in_eval; I32 base; } vmg_svt_free_cleanup_ud; @@ -1460,6 +1465,15 @@ static int vmg_svt_free_cleanup(pTHX_ void *ud_) { SV *sv = ud->sv; MAGIC *mg; + /* Silently undo the ref - don't trigger destruction in the referent + * for a second time */ + if (SvROK(ud->rsv) && SvRV(ud->rsv) == sv) { + --SvREFCNT(sv); + SvRV_set(ud->rsv, NULL); + SvROK_off(ud->rsv); + } + SvREFCNT_dec_NN(ud->rsv); + /* We are about to croak() while sv is being destroyed. Try to clean up * things a bit. */ mg = SvMAGIC(sv); @@ -1467,7 +1481,7 @@ static int vmg_svt_free_cleanup(pTHX_ void *ud_) { vmg_mg_del(sv, NULL, mg, mg->mg_moremagic); mg_magical(sv); } - SvREFCNT_dec(sv); + SvREFCNT_dec(sv); /* Re-trigger destruction */ vmg_dispell_guard_oncroak(aTHX_ NULL); @@ -1517,7 +1531,9 @@ static int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) { PUSHMARK(SP); EXTEND(SP, 2); - PUSHs(sv_2mortal(newRV_inc(sv))); + /* This will bump the refcount of sv from 0 to 1 */ + ud.rsv = newRV_inc(sv); + PUSHs(ud.rsv); PUSHs(mg->mg_obj ? mg->mg_obj : &PL_sv_undef); if (w->opinfo) XPUSHs(vmg_op_info(w->opinfo)); @@ -1544,6 +1560,15 @@ static int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) { POPSTACK; + /* Silently undo the ref - don't trigger destruction in the referent + * for a second time */ + if (SvROK(ud.rsv) && SvRV(ud.rsv) == sv) { + SvRV_set(ud.rsv, NULL); + SvROK_off(ud.rsv); + --SvREFCNT(sv); /* silent */ + } + SvREFCNT_dec_NN(ud.rsv); + FREETMPS; LEAVE;