From aa7dc2df7e74fcd83f494ca78a54c7961432b63d Mon Sep 17 00:00:00 2001 From: Richard Leach Date: Wed, 26 Aug 2020 23:15:34 +0100 Subject: [PATCH] pp_split: assign to temp AV in @ary = split(...) optimisation --- pp.c | 35 ++++++++++++++++++++++++++++++++++- t/op/split.t | 9 ++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/pp.c b/pp.c index f836b1685fd8..e7f3bb5caf03 100644 --- a/pp.c +++ b/pp.c @@ -5995,12 +5995,14 @@ PP(pp_split) I32 trailing_empty = 0; const char *orig; const IV origlimit = limit; + AV *tmp4ary; I32 realarray = 0; I32 base; const U8 gimme = GIMME_V; bool gimme_scalar; I32 oldsave = PL_savestack_ix; U32 make_mortal = SVs_TEMP; + bool switchstack = 0; bool multiline = 0; MAGIC *mg = NULL; @@ -6052,8 +6054,14 @@ PP(pp_split) AvARRAY(ary)[i] = &PL_sv_undef; /* don't free mere refs */ } /* temporarily switch stacks */ - SAVESWITCHSTACK(PL_curstack, ary); make_mortal = 0; + switchstack = 1; + /* tmp4ary is used as a placeholder, in case ary is modified */ + /* during the split, which could otherwise result in segfault */ + /* or panic. */ + tmp4ary = newAV(); + av_extend(tmp4ary, 0); + SAVESWITCHSTACK(PL_curstack, tmp4ary); } } @@ -6384,6 +6392,31 @@ PP(pp_split) LEAVE_SCOPE(oldsave); /* may undo an earlier SWITCHSTACK */ SPAGAIN; if (realarray) { + if (switchstack) { + /* The in-place optimization was triggered. */ + /* tmp4ary contains the *SV array resulting from the split. */ + /* The *SV arrays of tmp4ary and ary must now be swapped over.*/ + + SV** tmp1 = AvARRAY(ary); + SV** tmp2 = AvALLOC(ary); + SSize_t tmp3 = AvMAX(ary); + SSize_t tmp4 = AvFILLp(ary); + + AvARRAY(ary) = AvARRAY(tmp4ary); + AvARRAY(tmp4ary) = tmp1; + + AvALLOC(ary) = AvALLOC(tmp4ary); + AvALLOC(tmp4ary) = tmp2; + + AvMAX(ary) = AvMAX(tmp4ary); + AvMAX(tmp4ary) = tmp3; + + AvFILLp(ary) = AvFILLp(tmp4ary); + AvFILLp(tmp4ary) = tmp4; + + (void)sv_2mortal((SV*)tmp4ary); + } + if (!mg) { if (SvSMAGICAL(ary)) { PUTBACK; diff --git a/t/op/split.t b/t/op/split.t index 14f91584448c..42139d5acae2 100644 --- a/t/op/split.t +++ b/t/op/split.t @@ -7,7 +7,7 @@ BEGIN { set_up_inc('../lib'); } -plan tests => 176; +plan tests => 178; $FS = ':'; @@ -667,3 +667,10 @@ CODE ok(eq_array(\@result,['a','b']), "Resulting in ('a','b')"); } } + +# check that the (@ary = split) optimisation survives @ary being modified + +fresh_perl_is('my @ary; @ary = split(/\w(?{ @ary[1000] = 1 })/, "abc");', + '',{},'(@ary = split ...) survives @ary being Renew()ed'); +fresh_perl_is('my @ary; @ary = split(/\w(?{ undef @ary })/, "abc");', + '',{},'(@ary = split ...) survives an (undef @ary)');