From: Vincent Pit Date: Sun, 4 Jan 2009 14:13:34 +0000 (+0100) Subject: Fix deletion of localized array elements with negative indices resulting in an incorr... X-Git-Tag: v0.03~2 X-Git-Url: http://git.vpit.fr/?a=commitdiff_plain;h=5327334c349244e4e8e49239528400902677075a;p=perl%2Fmodules%2FScope-Upper.git Fix deletion of localized array elements with negative indices resulting in an incorrect restore at scope end --- diff --git a/Upper.xs b/Upper.xs index 657f01c..0235cbe 100644 --- a/Upper.xs +++ b/Upper.xs @@ -54,6 +54,10 @@ # define PERL_MAGIC_env 'E' #endif +#ifndef NEGATIVE_INDICES_VAR +# define NEGATIVE_INDICES_VAR "NEGATIVE_INDICES" +#endif + #define SU_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S)))))) /* --- Stack manipulations ------------------------------------------------- */ @@ -71,29 +75,58 @@ /* ... Saving array elements ............................................... */ +STATIC I32 su_av_key2idx(pTHX_ AV *av, I32 key) { +#define su_av_key2idx(A, K) su_av_key2idx(aTHX_ (A), (K)) + I32 idx; + + if (key >= 0) + return key; + +/* Added by MJD in perl-5.8.1 with 6f12eb6d2a1dfaf441504d869b27d2e40ef4966a */ +#if SU_HAS_PERL(5, 8, 1) + if (SvRMAGICAL(av)) { + const MAGIC * const tied_magic = mg_find((SV *) av, PERL_MAGIC_tied); + if (tied_magic) { + int adjust_index = 1; + SV * const * const negative_indices_glob = + hv_fetch(SvSTASH(SvRV(SvTIED_obj((SV *) (av), tied_magic))), + NEGATIVE_INDICES_VAR, 16, 0); + if (negative_indices_glob && SvTRUE(GvSV(*negative_indices_glob))) + return key; + } + } +#endif + + idx = key + av_len(av) + 1; + if (idx < 0) + return key; + + return idx; +} + #ifndef SAVEADELETE typedef struct { AV *av; - I32 key; + I32 idx; } su_ud_adelete; STATIC void su_adelete(pTHX_ void *ud_) { su_ud_adelete *ud = ud_; - av_delete(ud->av, ud->key, G_DISCARD); + av_delete(ud->av, ud->idx, G_DISCARD); SvREFCNT_dec(ud->av); Safefree(ud); } -STATIC void su_save_adelete(pTHX_ AV *av, I32 key) { +STATIC void su_save_adelete(pTHX_ AV *av, I32 idx) { #define su_save_adelete(A, K) su_save_adelete(aTHX_ (A), (K)) su_ud_adelete *ud; Newx(ud, 1, su_ud_adelete); ud->av = av; - ud->key = key; + ud->idx = idx; SvREFCNT_inc(av); SAVEDESTRUCTOR_X(su_adelete, ud); @@ -105,12 +138,14 @@ STATIC void su_save_adelete(pTHX_ AV *av, I32 key) { STATIC void su_save_aelem(pTHX_ AV *av, SV *key, SV *val) { #define su_save_aelem(A, K, V) su_save_aelem(aTHX_ (A), (K), (V)) - I32 idx = SvIV(key); + I32 idx; I32 preeminent = 1; SV **svp; HV *stash; MAGIC *mg; + idx = su_av_key2idx(av, SvIV(key)); + if (SvCANEXISTDELETE(av)) preeminent = av_exists(av, idx); diff --git a/t/48-localize_delete-magic.t b/t/48-localize_delete-magic.t index f037b3b..0b93050 100644 --- a/t/48-localize_delete-magic.t +++ b/t/48-localize_delete-magic.t @@ -5,7 +5,7 @@ use warnings; use Scope::Upper qw/localize_delete/; -use Test::More tests => 5; +use Test::More tests => 9; our $deleted; @@ -19,6 +19,8 @@ our $deleted; sub FETCHSIZE { scalar @{$_[0]} } sub DELETE { ++$main::deleted; delete $_[0]->[$_[1]] } sub EXTEND {} + + our $NEGATIVE_INDICES = 0; } our @a; @@ -36,3 +38,26 @@ tie @a, 'Scope::Upper::Test::TiedArray'; is_deeply \@a, [ 5 .. 7, undef, 9 ], 'localize_elem @incomplete_tied_array, $nonexistent, 12 => 0 [end]'; is $deleted, 1, 'localize_delete @tied_array, $existent => 0 [not more deleted]'; } + +{ + local @a = (4 .. 6); + local $a[4] = 7; + { + localize_delete '@main::a', -1, 0; + is_deeply \@a, [ 4 .. 6 ], 'localize_delete @tied_array, $existent_neg => 0 [ok]'; + } + is_deeply \@a, [ 4 .. 6, undef, 7 ], 'localize_delete @tied_array, $existent_neg => 0 [end]'; +} + +SKIP: +{ + skip '$NEGATIVE_INDICES has no special meaning on 5.8.0 and older' => 2 if $] < 5.008_001; + local $Scope::Upper::Test::TiedArray::NEGATIVE_INDICES = 1; + local @a = (4 .. 6); + local $a[4] = 7; + { + localize_delete '@main::a', -1, 0; + is_deeply \@a, [ 4 .. 6 ], 'localize_delete @tied_array_wo_neg, $existent_neg => 0 [ok]'; + } + is_deeply \@a, [ 4, 5, 7 ], 'localize_delete @tied_array_wo_neg, $existent_neg => 0 [end]'; +} diff --git a/t/49-localize_delete-target.t b/t/49-localize_delete-target.t index 6ddf79c..30468a1 100644 --- a/t/49-localize_delete-target.t +++ b/t/49-localize_delete-target.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 30; +use Test::More tests => 36; use Scope::Upper qw/localize_delete/; @@ -39,6 +39,34 @@ our @a; is_deeply \@a, [ 4 .. 6, undef, 7 ], 'localize_delete "@a", 4 (exists), 0 [end]'; } +{ + local @a = (4 .. 6); + { + localize_delete '@main::a', -2, 0; + is_deeply \@a, [ 4, undef, 6 ], 'localize_delete "@a", -2, 0 [ok]'; + } + is_deeply \@a, [ 4 .. 6 ], 'localize_delete "@a", -2, 0 [end]'; +} + +{ + local @a = (4 .. 6); + local $a[4] = 7; + { + localize_delete '@main::a', -1, 0; + is_deeply \@a, [ 4 .. 6 ], 'localize_delete "@a", -1 (exists), 0 [ok]'; + } + is_deeply \@a, [ 4 .. 6, undef, 7 ], 'localize_delete "@a", -1 (exists), 0 [end]'; +} + +{ + local @a = (4 .. 6); + { + eval { localize_delete '@main::a', -4, 0 }; + like $@, qr/Modification of non-creatable array value attempted, subscript -4/, 'localize_delete "@a", -4 (out of bounds), 0 [ok]'; + } + is_deeply \@a, [ 4 .. 6 ], 'localize_delete "@a", -4 (out of bounds), 0 [end]'; +} + { local @a = (4 .. 6); {