if (SvTYPE(av) == SVt_PVAV) {
const I32 arybase = CopARYBASE_get(PL_curcop);
- if (lval && PL_op->op_private & OPpLVAL_INTRO) {
+ const bool localizing = PL_op->op_private & OPpLVAL_INTRO;
+ bool can_preserve = FALSE;
+
+ if (localizing) {
+ MAGIC *mg;
+ HV *stash;
+
+ can_preserve = SvCANEXISTDELETE(av);
+ }
+
+ if (lval && localizing) {
register SV **svp;
I32 max = -1;
for (svp = MARK + 1; svp <= SP; svp++) {
if (max > AvMAX(av))
av_extend(av, max);
}
+
while (++MARK <= SP) {
register SV **svp;
I32 elem = SvIV(*MARK);
+ bool preeminent = TRUE;
if (elem > 0)
elem -= arybase;
+ if (localizing && can_preserve) {
+ /* If we can determine whether the element exist,
+ * Try to preserve the existenceness of a tied array
+ * element by using EXISTS and DELETE if possible.
+ * Fallback to FETCH and STORE otherwise. */
+ preeminent = av_exists(av, elem);
+ }
+
svp = av_fetch(av, elem, lval);
if (lval) {
if (!svp || *svp == &PL_sv_undef)
DIE(aTHX_ PL_no_aelem, elem);
- if (PL_op->op_private & OPpLVAL_INTRO)
- save_aelem(av, elem, svp);
+ if (localizing) {
+ if (preeminent)
+ save_aelem(av, elem, svp);
+ else
+ SAVEADELETE(av, elem);
+ }
}
*MARK = svp ? *svp : &PL_sv_undef;
}
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 bool localizing = PL_op->op_private & OPpLVAL_INTRO;
+ bool preeminent = TRUE;
SV *sv;
if (SvROK(elemsv) && !SvGAMAGIC(elemsv) && ckWARN(WARN_MISC))
elem -= CopARYBASE_get(PL_curcop);
if (SvTYPE(av) != SVt_PVAV)
RETPUSHUNDEF;
+
+ if (localizing) {
+ MAGIC *mg;
+ HV *stash;
+
+ /* If we can determine whether the element exist,
+ * Try to preserve the existenceness of a tied array
+ * element by using EXISTS and DELETE if possible.
+ * Fallback to FETCH and STORE otherwise. */
+ if (SvCANEXISTDELETE(av))
+ preeminent = av_exists(av, elem);
+ }
+
svp = av_fetch(av, elem, lval && !defer);
if (lval) {
#ifdef PERL_MALLOC_WRAP
PUSHs(lv);
RETURN;
}
- if (PL_op->op_private & OPpLVAL_INTRO)
- save_aelem(av, elem, svp);
+ if (localizing) {
+ if (preeminent)
+ save_aelem(av, elem, svp);
+ else
+ SAVEADELETE(av, elem);
+ }
else if (PL_op->op_private & OPpDEREF)
vivify_ref(*svp, PL_op->op_private & OPpDEREF);
}
@INC = qw(. ../lib);
require './test.pl';
}
-plan tests => 123;
+plan tests => 183;
my $list_assignment_supported = 1;
@a = ('a', 'b', 'c');
{
+ local($a[4]) = 'x';
+ ok(!defined $a[3]);
+ is($a[4], 'x');
+}
+is(scalar(@a), 3);
+ok(!exists $a[3]);
+ok(!exists $a[4]);
+
+@a = ('a', 'b', 'c');
+{
+ local($a[5]) = 'z';
+ $a[4] = 'y';
+ ok(!defined $a[3]);
+ is($a[4], 'y');
+ is($a[5], 'z');
+}
+is(scalar(@a), 5);
+ok(!defined $a[3]);
+is($a[4], 'y');
+ok(!exists $a[5]);
+
+@a = ('a', 'b', 'c');
+{
+ local(@a[4,6]) = ('x', 'z');
+ ok(!defined $a[3]);
+ is($a[4], 'x');
+ ok(!defined $a[5]);
+ is($a[6], 'z');
+}
+is(scalar(@a), 3);
+ok(!exists $a[3]);
+ok(!exists $a[4]);
+ok(!exists $a[5]);
+ok(!exists $a[6]);
+
+@a = ('a', 'b', 'c');
+{
+ local(@a[4,6]) = ('x', 'z');
+ $a[5] = 'y';
+ ok(!defined $a[3]);
+ is($a[4], 'x');
+ is($a[5], 'y');
+ is($a[6], 'z');
+}
+is(scalar(@a), 6);
+ok(!defined $a[3]);
+ok(!defined $a[4]);
+is($a[5], 'y');
+ok(!exists $a[6]);
+
+@a = ('a', 'b', 'c');
+{
local($a[1]) = "X";
shift @a;
}
sub TIEARRAY { bless [], $_[0] }
sub STORE { print "# STORE [@_]\n"; $_[0]->[$_[1]] = $_[2] }
sub FETCH { my $v = $_[0]->[$_[1]]; print "# FETCH [@_=$v]\n"; $v }
+ sub EXISTS { print "# EXISTS [@_]\n"; exists $_[0]->[$_[1]]; }
+ sub DELETE { print "# DELETE [@_]\n"; delete $_[0]->[$_[1]]; }
sub CLEAR { print "# CLEAR [@_]\n"; @{$_[0]} = (); }
sub FETCHSIZE { scalar(@{$_[0]}) }
sub SHIFT { shift (@{$_[0]}) }
is("@a", $d);
}
+# local() should preserve the existenceness of tied array elements
+@a = ('a', 'b', 'c');
+{
+ local($a[4]) = 'x';
+ ok(!defined $a[3]);
+ is($a[4], 'x');
+}
+is(scalar(@a), 3);
+ok(!exists $a[3]);
+ok(!exists $a[4]);
+
+@a = ('a', 'b', 'c');
+{
+ local($a[5]) = 'z';
+ $a[4] = 'y';
+ ok(!defined $a[3]);
+ is($a[4], 'y');
+ is($a[5], 'z');
+}
+is(scalar(@a), 5);
+ok(!defined $a[3]);
+is($a[4], 'y');
+ok(!exists $a[5]);
+
+@a = ('a', 'b', 'c');
+{
+ local(@a[4,6]) = ('x', 'z');
+ ok(!defined $a[3]);
+ is($a[4], 'x');
+ ok(!defined $a[5]);
+ is($a[6], 'z');
+}
+is(scalar(@a), 3);
+ok(!exists $a[3]);
+ok(!exists $a[4]);
+ok(!exists $a[5]);
+ok(!exists $a[6]);
+
+@a = ('a', 'b', 'c');
+{
+ local(@a[4,6]) = ('x', 'z');
+ $a[5] = 'y';
+ ok(!defined $a[3]);
+ is($a[4], 'x');
+ is($a[5], 'y');
+ is($a[6], 'z');
+}
+is(scalar(@a), 6);
+ok(!defined $a[3]);
+ok(!defined $a[4]);
+is($a[5], 'y');
+ok(!exists $a[6]);
+
+# see if localization works on tied hashes
{
package TH;
sub TIEHASH { bless {}, $_[0] }
sub NEXTKEY { print "# NEXTKEY [@_]\n"; each %{$_[0]} }
}
-# see if localization works on tied hashes
tie %h, 'TH';
%h = ('a' => 1, 'b' => 2, 'c' => 3);