diff options
author | Father Chrysostomos <sprout@cpan.org> | 2018-02-18 16:12:11 -0800 |
---|---|---|
committer | Father Chrysostomos <sprout@cpan.org> | 2018-02-18 16:29:35 -0800 |
commit | 9ef753fe465d865deeba96157242bc98c0afe412 (patch) | |
tree | 15d7e5eb8eae642b4fccd1872bf40e80f3e9eb1c /pp_hot.c | |
parent | 7406cffe8ec122fcc2500115f8ed1742385893e1 (diff) | |
download | perl-9ef753fe465d865deeba96157242bc98c0afe412.tar.gz |
Fix ary shifting when sparse ary is passed to sub
This commit fixes #132729 in the specific case where a nonexistent
element within a sparse array is passed to a subroutine.
Prior to this commit,
some_sub($sparse_array[$n])
where $n <= $#sparse_array and the element does not exist, would exhi-
bit erroneous behaviour if some_sub shifted or unshifted the original
@sparse_array. Any ‘holes’ (nonexistent elements) in the array would
show up in @_ as deferred element (defelem) scalars, magic scalars
that remember their index in the array. This index is not updated and
gets out of synch when the array is shifted.
This commit fixes the bug for elements within the array by using the
new ‘nonelem’ magic introduced a few commits ago. It stores within
the array a magic scalar that is marked as being nonexistent.
It also reduced the number of scalars that need to be created if such
a sub call happens repeatedly.
Diffstat (limited to 'pp_hot.c')
-rw-r--r-- | pp_hot.c | 38 |
1 files changed, 25 insertions, 13 deletions
@@ -3630,13 +3630,19 @@ PP(pp_multideref) if (!defer) DIE(aTHX_ PL_no_aelem, elem); len = av_tindex(av); - sv = sv_2mortal(newSVavdefelem(av, - /* Resolve a negative index now, unless it points - * before the beginning of the array, in which - * case record it for error reporting in - * magic_setdefelem. */ - elem < 0 && len + elem >= 0 - ? len + elem : elem, 1)); + /* Resolve a negative index that falls within + * the array. Leave it negative it if falls + * outside the array. */ + if (elem < 0 && len + elem >= 0) + elem = len + elem; + if (elem >= 0 && elem <= len) + /* Falls within the array. */ + sv = av_nonelem(av,elem); + else + /* Falls outside the array. If it is neg- + ative, magic_setdefelem will use the + index for error reporting. */ + sv = sv_2mortal(newSVavdefelem(av,elem,1)); } else { if (UNLIKELY(localizing)) { @@ -5363,12 +5369,18 @@ PP(pp_aelem) if (!defer) DIE(aTHX_ PL_no_aelem, elem); len = av_tindex(av); - mPUSHs(newSVavdefelem(av, - /* Resolve a negative index now, unless it points before the - beginning of the array, in which case record it for error - reporting in magic_setdefelem. */ - elem < 0 && len + elem >= 0 ? len + elem : elem, - 1)); + /* Resolve a negative index that falls within the array. Leave + it negative it if falls outside the array. */ + if (elem < 0 && len + elem >= 0) + elem = len + elem; + if (elem >= 0 && elem <= len) + /* Falls within the array. */ + PUSHs(av_nonelem(av,elem)); + else + /* Falls outside the array. If it is negative, + magic_setdefelem will use the index for error reporting. + */ + mPUSHs(newSVavdefelem(av, elem, 1)); RETURN; } if (UNLIKELY(localizing)) { |