summaryrefslogtreecommitdiff
path: root/pp_hot.c
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2013-07-16 22:56:44 -0700
committerFather Chrysostomos <sprout@cpan.org>2013-08-21 00:03:55 -0700
commitbbfdc870734e1313430ade6e6bd6d8ee2b720413 (patch)
treece4190735fd46ebdaf7db844ee9f4bc338ad3a0e /pp_hot.c
parentb2d74da6dac4b0c1c9ebf65c8d96a3ee53f5232f (diff)
downloadperl-bbfdc870734e1313430ade6e6bd6d8ee2b720413.tar.gz
[perl #118691] Allow defelem magic with neg indices
When a nonexistent array element is passed to a subroutine, a special ‘deferred element’ scalar (implemented using something called defelem magic) is passed to the subroutine instead, which delegates to the array element. This allows some_benign_function($array[$nonexistent]) to avoid autovivifying unnecessarily. Whether this magic would be triggered was based on whether the element was within the range 0..$#array. Since arrays can contain nonexistent elements before $#array, this logic is incorrect. It also makes sense to allow $array[$neg] where the negative number points before the beginning of the array to create a deferred element and only croak if it is assigned to. This commit fixes the logic for when deferred elements are created and implements these deferred negative elements. Since we have to be able to store negative values in xlv_targoff, it is convenient to make it a union (with two types--signed and unsigned) and use LvSTARGOFF for defelem array indices.
Diffstat (limited to 'pp_hot.c')
-rw-r--r--pp_hot.c10
1 files changed, 8 insertions, 2 deletions
diff --git a/pp_hot.c b/pp_hot.c
index 58a30831d8..571cd63b0a 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -2795,7 +2795,7 @@ PP(pp_aelem)
IV elem = SvIV(elemsv);
AV *const av = MUTABLE_AV(POPs);
const U32 lval = PL_op->op_flags & OPf_MOD || LVRET;
- const U32 defer = (PL_op->op_private & OPpLVAL_DEFER) && (elem > av_len(av));
+ const U32 defer = PL_op->op_private & OPpLVAL_DEFER;
const bool localizing = PL_op->op_private & OPpLVAL_INTRO;
bool preeminent = TRUE;
SV *sv;
@@ -2836,14 +2836,20 @@ PP(pp_aelem)
#endif
if (!svp || !*svp) {
SV* lv;
+ IV len;
if (!defer)
DIE(aTHX_ PL_no_aelem, elem);
+ len = av_len(av);
lv = sv_newmortal();
sv_upgrade(lv, SVt_PVLV);
LvTYPE(lv) = 'y';
sv_magic(lv, NULL, PERL_MAGIC_defelem, NULL, 0);
LvTARG(lv) = SvREFCNT_inc_simple(av);
- LvTARGOFF(lv) = elem;
+ /* 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. */
+ LvSTARGOFF(lv) =
+ elem < 0 && len + elem >= 0 ? len + elem : elem;
LvTARGLEN(lv) = 1;
PUSHs(lv);
RETURN;