#endif
key = AvMAX(av) + 1;
while (key > AvFILLp(av) + 1)
- AvARRAY(av)[--key] = &PL_sv_undef;
+ AvARRAY(av)[--key] = NULL;
while (key) {
SV * const sv = AvARRAY(av)[--key];
- assert(sv);
if (sv != &PL_sv_undef)
- SvREFCNT_inc_simple_void_NN(sv);
+ SvREFCNT_inc_simple_void(sv);
}
key = AvARRAY(av) - AvALLOC(av);
while (key)
- AvALLOC(av)[--key] = &PL_sv_undef;
+ AvALLOC(av)[--key] = NULL;
AvREIFY_off(av);
AvREAL_on(av);
}
*arrayp = *allocp;
if (AvREAL(av)) {
while (tmp)
- ary[--tmp] = &PL_sv_undef;
+ ary[--tmp] = NULL;
}
if (key > *maxp - 10) {
newmax = key + *maxp;
based on calling Perl_safesysmalloc_size() immediately after
allocation, I'm not convinced that it is a great idea here.
In an array we have to loop round setting everything to
- &PL_sv_undef, which means writing to memory, potentially lots
+ NULL, which means writing to memory, potentially lots
of it, whereas for the SV buffer case we don't touch the
"bonus" memory. So there there is no cost in telling the
world about it, whereas here we have to do work before we can
Newx(*allocp, newmax+1, SV*);
ary = *allocp + 1;
tmp = newmax;
- *allocp[0] = &PL_sv_undef; /* For the stacks */
+ *allocp[0] = NULL; /* For the stacks */
}
if (av && AvREAL(av)) {
while (tmp)
- ary[--tmp] = &PL_sv_undef;
+ ary[--tmp] = NULL;
}
*arrayp = *allocp;
return NULL;
}
- if (key > AvFILLp(av) || AvARRAY(av)[key] == &PL_sv_undef) {
+ if (key > AvFILLp(av) || !AvARRAY(av)[key]) {
emptyness:
return lval ? av_store(av,key,newSV(0)) : NULL;
}
if (AvREIFY(av)
&& (!AvARRAY(av)[key] /* eg. @_ could have freed elts */
|| SvIS_FREED(AvARRAY(av)[key]))) {
- AvARRAY(av)[key] = &PL_sv_undef; /* 1/2 reify */
+ AvARRAY(av)[key] = NULL; /* 1/2 reify */
goto emptyness;
}
return &AvARRAY(av)[key];
(unicode_alternate may be NULL).
*/
- if (!val)
- val = &PL_sv_undef;
-
if (SvRMAGICAL(av)) {
const MAGIC * const tied_magic = mg_find((const SV *)av, PERL_MAGIC_tied);
if (tied_magic) {
if (!S_adjust_index(aTHX_ av, tied_magic, &key))
return 0;
}
- if (val != &PL_sv_undef) {
+ if (val) {
mg_copy(MUTABLE_SV(av), val, 0, key);
}
return NULL;
if (av == PL_curstack && key > PL_stack_sp - PL_stack_base)
PL_stack_sp = PL_stack_base + key; /* XPUSH in disguise */
do {
- ary[++AvFILLp(av)] = &PL_sv_undef;
+ ary[++AvFILLp(av)] = NULL;
} while (AvFILLp(av) < key);
}
AvFILLp(av) = key;
bool set = TRUE;
for (; mg; mg = mg->mg_moremagic) {
if (!isUPPER(mg->mg_type)) continue;
- if (val != &PL_sv_undef) {
+ if (val) {
sv_magic(val, MUTABLE_SV(av), toLOWER(mg->mg_type), 0, key);
}
if (PL_delaymagic && mg->mg_type == PERL_MAGIC_isa) {
SV * const sv = ary[--index];
/* undef the slot before freeing the value, because a
* destructor might try to modify this array */
- ary[index] = &PL_sv_undef;
+ ary[index] = NULL;
SvREFCNT_dec(sv);
}
}
if (AvFILL(av) < 0)
return &PL_sv_undef;
retval = AvARRAY(av)[AvFILLp(av)];
- AvARRAY(av)[AvFILLp(av)--] = &PL_sv_undef;
+ AvARRAY(av)[AvFILLp(av)--] = NULL;
if (SvSMAGICAL(av))
mg_set(MUTABLE_SV(av));
- return retval;
+ return retval ? retval : &PL_sv_undef;
}
/*
ary = AvARRAY(av);
Move(ary, ary + num, i + 1, SV*);
do {
- ary[--num] = &PL_sv_undef;
+ ary[--num] = NULL;
} while (num);
/* Make extra elements into a buffer */
AvMAX(av) -= slide;
return &PL_sv_undef;
retval = *AvARRAY(av);
if (AvREAL(av))
- *AvARRAY(av) = &PL_sv_undef;
+ *AvARRAY(av) = NULL;
AvARRAY(av) = AvARRAY(av) + 1;
AvMAX(av)--;
AvFILLp(av)--;
if (SvSMAGICAL(av))
mg_set(MUTABLE_SV(av));
- return retval;
+ return retval ? retval : &PL_sv_undef;
}
/*
The number of elements in the an array will be C<fill + 1> after
av_fill() returns. If the array was previously shorter, then the
-additional elements appended are set to C<PL_sv_undef>. If the array
+additional elements appended are set to NULL. If the array
was longer, then the excess elements are freed. C<av_fill(av, -1)> is
the same as C<av_clear(av)>.
if (AvREAL(av)) {
while (key > fill) {
SvREFCNT_dec(ary[key]);
- ary[key--] = &PL_sv_undef;
+ ary[key--] = NULL;
}
}
else {
while (key < fill)
- ary[++key] = &PL_sv_undef;
+ ary[++key] = NULL;
}
AvFILLp(av) = fill;
mg_set(MUTABLE_SV(av));
}
else
- (void)av_store(av,fill,&PL_sv_undef);
+ (void)av_store(av,fill,NULL);
}
/*
av_reify(av);
sv = AvARRAY(av)[key];
if (key == AvFILLp(av)) {
- AvARRAY(av)[key] = &PL_sv_undef;
+ AvARRAY(av)[key] = NULL;
do {
AvFILLp(av)--;
- } while (--key >= 0 && AvARRAY(av)[key] == &PL_sv_undef);
+ } while (--key >= 0 && !AvARRAY(av)[key]);
}
else
- AvARRAY(av)[key] = &PL_sv_undef;
+ AvARRAY(av)[key] = NULL;
if (SvSMAGICAL(av))
mg_set(MUTABLE_SV(av));
}
Returns true if the element indexed by C<key> has been initialized.
This relies on the fact that uninitialized array elements are set to
-C<&PL_sv_undef>.
+NULL.
Perl equivalent: C<exists($myarray[$key])>.
return FALSE;
}
- if (key <= AvFILLp(av) && AvARRAY(av)[key] != &PL_sv_undef
- && AvARRAY(av)[key])
+ if (key <= AvFILLp(av) && AvARRAY(av)[key])
{
return TRUE;
}
#define SX_WEAKOVERLOAD C(28) /* Overloaded weak reference */
#define SX_VSTRING C(29) /* vstring forthcoming (small) */
#define SX_LVSTRING C(30) /* vstring forthcoming (large) */
-#define SX_ERROR C(31) /* Error */
+#define SX_SVUNDEF_ELEM C(31) /* array element set to &PL_sv_undef */
+#define SX_ERROR C(32) /* Error */
/*
* Those are only used to retrieve "old" pre-0.6 binary images.
#endif
#define STORABLE_BIN_MAJOR 2 /* Binary major "version" */
-#define STORABLE_BIN_MINOR 9 /* Binary minor "version" */
+#define STORABLE_BIN_MINOR 10 /* Binary minor "version" */
#if (PATCHLEVEL <= 5)
#define STORABLE_BIN_WRITE_MINOR 4
* Perl 5.6.0-5.8.0 can do weak references, but not vstring magic.
*/
#define STORABLE_BIN_WRITE_MINOR 8
+#elif PATCHLEVEL >= 19
+/* Perl 5.19 takes away the special meaning of PL_sv_undef in arrays. */
+#define STORABLE_BIN_WRITE_MINOR 10
#else
#define STORABLE_BIN_WRITE_MINOR 9
#endif /* (PATCHLEVEL <= 5) */
#define STORE_SCALAR(pv, len) STORE_PV_LEN(pv, len, SX_SCALAR, SX_LSCALAR)
/*
- * Store &PL_sv_undef in arrays without recursing through store().
+ * Store &PL_sv_undef in arrays without recursing through store(). We
+ * actually use this to represent nonexistent elements, for historical
+ * reasons.
*/
#define STORE_SV_UNDEF() \
STMT_START { \
(sv_retrieve_t)retrieve_other, /* SX_WEAKOVERLOAD not supported */
(sv_retrieve_t)retrieve_other, /* SX_VSTRING not supported */
(sv_retrieve_t)retrieve_other, /* SX_LVSTRING not supported */
+ (sv_retrieve_t)retrieve_other, /* SX_SVUNDEF_ELEM not supported */
(sv_retrieve_t)retrieve_other, /* SX_ERROR */
};
static SV *retrieve_weakoverloaded(pTHX_ stcxt_t *cxt, const char *cname);
static SV *retrieve_vstring(pTHX_ stcxt_t *cxt, const char *cname);
static SV *retrieve_lvstring(pTHX_ stcxt_t *cxt, const char *cname);
+static SV *retrieve_svundef_elem(pTHX_ stcxt_t *cxt, const char *cname);
static const sv_retrieve_t sv_retrieve[] = {
0, /* SX_OBJECT -- entry unused dynamically */
(sv_retrieve_t)retrieve_weakoverloaded, /* SX_WEAKOVERLOAD */
(sv_retrieve_t)retrieve_vstring, /* SX_VSTRING */
(sv_retrieve_t)retrieve_lvstring, /* SX_LVSTRING */
+ (sv_retrieve_t)retrieve_svundef_elem, /* SX_SVUNDEF_ELEM */
(sv_retrieve_t)retrieve_other, /* SX_ERROR */
};
for (i = 0; i < len; i++) {
sav = av_fetch(av, i, 0);
if (!sav) {
- TRACEME(("(#%d) undef item", i));
+ TRACEME(("(#%d) nonexistent item", i));
STORE_SV_UNDEF();
continue;
}
+#if PATCHLEVEL >= 19
+ /* In 5.19.3 and up, &PL_sv_undef can actually be stored in
+ * an array; it no longer represents nonexistent elements.
+ * Historically, we have used SX_SV_UNDEF in arrays for
+ * nonexistent elements, so we use SX_SVUNDEF_ELEM for
+ * &PL_sv_undef itself. */
+ if (*sav == &PL_sv_undef) {
+ TRACEME(("(#%d) undef item", i));
+ cxt->tagnum++;
+ PUTMARK(SX_SVUNDEF_ELEM);
+ continue;
+ }
+#endif
TRACEME(("(#%d) item", i));
if ((ret = store(aTHX_ cxt, *sav))) /* Extra () for -Wall, grr... */
return ret;
}
/*
+ * retrieve_svundef_elem
+ *
+ * Return &PL_sv_placeholder, representing &PL_sv_undef in an array. This
+ * is a bit of a hack, but we already use SX_SV_UNDEF to mean a nonexistent
+ * element, for historical reasons.
+ */
+static SV *retrieve_svundef_elem(pTHX_ stcxt_t *cxt, const char *cname)
+{
+ TRACEME(("retrieve_svundef_elem"));
+
+ /* SEEN reads the contents of its SV argument, which we are not
+ supposed to do with &PL_sv_placeholder. */
+ SEEN(&PL_sv_undef, cname, 1);
+
+ return &PL_sv_placeholder;
+}
+
+/*
* retrieve_array
*
* Retrieve a whole array.
AV *av;
SV *sv;
HV *stash;
+ bool seen_null = FALSE;
TRACEME(("retrieve_array (#%d)", cxt->tagnum));
sv = retrieve(aTHX_ cxt, 0); /* Retrieve item */
if (!sv)
return (SV *) 0;
+ if (sv == &PL_sv_undef) {
+ seen_null = TRUE;
+ continue;
+ }
+ if (sv == &PL_sv_placeholder)
+ sv = &PL_sv_undef;
if (av_store(av, i, sv) == 0)
return (SV *) 0;
}
+ if (seen_null) av_fill(av, len-1);
TRACEME(("ok (retrieve_array at 0x%"UVxf")", PTR2UV(av)));
$other_magic = 7 + length $byteorder;
$network_magic = 2;
$major = 2;
-$minor = 9;
-$minor_write = $] > 5.008 ? 9 : $] > 5.005_50 ? 8 : 4;
+$minor = 10;
+$minor_write = $] >= 5.019 ? 10 : $] > 5.008 ? 9 : $] > 5.005_50 ? 8 : 4;
use Test::More;
$where = $file_magic + $network_magic;
}
- # Just the header and a tag 255. As 30 is currently the highest tag, this
+ # Just the header and a tag 255. As 31 is currently the highest tag, this
# is "unexpected"
$copy = substr ($contents, 0, $where) . chr 255;
# local $Storable::DEBUGME = 1;
# This is the delayed croak
test_corrupt ($copy, $sub,
- "/^Storable binary image v$header->{major}.$minor6 contains data of type 255. This Storable is v$header->{major}.$minor and can only handle data types up to 30/",
+ "/^Storable binary image v$header->{major}.$minor6 contains data of type 255. This Storable is v$header->{major}.$minor and can only handle data types up to 31/",
"bogus tag, minor plus 4");
# And check again that this croak is not delayed:
{
ok "# use5005threads: test skipped\n";
} else {
$a = `$^X $path "-MO=Showlex" -e "my \@one" 2>&1`;
- like ($a, qr/sv_undef.*PVNV.*\@one.*sv_undef.*AV/s,
+ like ($a, qr/sv_undef.*PVNV.*\@one.*Nullsv.*AV/s,
"canonical usage works");
}
}
}
for (j = 1; j < AvFILL((AV*)svp[1]); j++) { /* Vars. */
+ if (!pad[j]) continue;
if (SvROK(pad[j])) {
levelref++;
do_sv_dump(0, Perl_debug_log, pad[j], 0, 4, 0, 0);
# it's too ugly to use @_ throughout to make perl do it for us
# tchrist 5-Mar-00
+ # Historically, open3(undef...) has silently worked, so keep
+ # it working.
+ splice @_, 0, 1, undef if \$_[0] == \undef;
+ splice @_, 1, 1, undef if \$_[1] == \undef;
unless (eval {
$_[0] = gensym unless defined $_[0] && length $_[0];
$_[1] = gensym unless defined $_[1] && length $_[1];
av_store(pad, 0, NULL);
padname = newAV();
AvPAD_NAMELIST_on(padname);
+ av_store(padname, 0, &PL_sv_undef);
}
/* Most subroutines never recurse, hence only need 2 entries in the padlist
PL_curpad[po] = newSV(0);
SvPADTMP_on(PL_curpad[po]);
#else
- PL_curpad[po] = &PL_sv_undef;
+ PL_curpad[po] = NULL;
#endif
if (PadnamelistMAX(PL_comppad_name) != -1
&& (PADOFFSET)PadnamelistMAX(PL_comppad_name) >= po) {
- assert(!PadnameLEN(PadnamelistARRAY(PL_comppad_name)[po]));
+ if (PadnamelistARRAY(PL_comppad_name)[po]) {
+ assert(!PadnameLEN(PadnamelistARRAY(PL_comppad_name)[po]));
+ }
PadnamelistARRAY(PL_comppad_name)[po] = &PL_sv_undef;
}
if ((I32)po < PL_padix)
av_store(PL_comppad_name, AvFILLp(PL_comppad), NULL);
if (type == padtidy_SUBCLONE) {
- SV * const * const namep = AvARRAY(PL_comppad_name);
+ SV ** const namep = AvARRAY(PL_comppad_name);
PADOFFSET ix;
for (ix = AvFILLp(PL_comppad); ix > 0; ix--) {
SV *namesv;
+ if (!namep[ix]) namep[ix] = &PL_sv_undef;
/*
* The only things that a clonable function needs in its
* pad are anonymous subs, constants and GVs.
* The rest are created anew during cloning.
*/
- if (SvIMMORTAL(PL_curpad[ix]) || IS_PADGV(PL_curpad[ix]))
+ if (!PL_curpad[ix] || SvIMMORTAL(PL_curpad[ix])
+ || IS_PADGV(PL_curpad[ix]))
continue;
- if (!((namesv = namep[ix]) != NULL &&
- PadnamePV(namesv) &&
+ namesv = namep[ix];
+ if (!(PadnamePV(namesv) &&
(!PadnameLEN(namesv) || *SvPVX_const(namesv) == '&')))
{
SvREFCNT_dec(PL_curpad[ix]);
}
if (type == padtidy_SUB || type == padtidy_FORMAT) {
- SV * const * const namep = AvARRAY(PL_comppad_name);
+ SV ** const namep = AvARRAY(PL_comppad_name);
PADOFFSET ix;
for (ix = AvFILLp(PL_comppad); ix > 0; ix--) {
- if (SvIMMORTAL(PL_curpad[ix]) || IS_PADGV(PL_curpad[ix]) || IS_PADCONST(PL_curpad[ix]))
+ if (!namep[ix]) namep[ix] = &PL_sv_undef;
+ if (!PL_curpad[ix] || SvIMMORTAL(PL_curpad[ix])
+ || IS_PADGV(PL_curpad[ix]) || IS_PADCONST(PL_curpad[ix]))
continue;
if (!SvPADMY(PL_curpad[ix])) {
SvPADTMP_on(PL_curpad[ix]);
for ( ;ix > 0; ix--) {
if (!oldpad[ix]) {
pad1a[ix] = NULL;
- } else if (names_fill >= ix && PadnameLEN(names[ix])) {
+ } else if (names_fill >= ix && names[ix] &&
+ PadnameLEN(names[ix])) {
const char sigil = SvPVX_const(names[ix])[0];
if ((SvFLAGS(names[ix]) & SVf_FAKE)
|| (SvFLAGS(names[ix]) & SVpad_STATE)
}
}
}
- else if (IS_PADGV(oldpad[ix]) || PadnamePV(names[ix])) {
+ else if (IS_PADGV(oldpad[ix])
+ || ( names_fill >= ix && names[ix]
+ && PadnamePV(names[ix]) )) {
pad1a[ix] = sv_dup_inc(oldpad[ix], param);
}
else {
svp = av_fetch(av, elem, lval);
if (lval) {
- if (!svp || *svp == &PL_sv_undef)
+ if (!svp || !*svp)
DIE(aTHX_ PL_no_aelem, elem);
if (localizing) {
if (preeminent)
}
i = -diff;
while (i)
- dst[--i] = &PL_sv_undef;
+ dst[--i] = NULL;
if (newlen) {
Copy( tmparyval, AvARRAY(ary) + offset, newlen, SV* );
}
}
else {
- Copy(AvARRAY(av), SP+1, maxarg, SV*);
+ U32 i;
+ for (i=0; i < (U32)maxarg; i++) {
+ SV * const sv = AvARRAY(av)[i];
+ SP[i+1] = sv ? sv : &PL_sv_undef;
+ }
}
SP += maxarg;
PUTBACK;
i = 0;
while (relem <= lastrelem) { /* gobble up all the rest */
SV **didstore;
- assert(*relem);
- SvGETMAGIC(*relem); /* before newSV, in case it dies */
+ if (*relem)
+ SvGETMAGIC(*relem); /* before newSV, in case it dies */
sv = newSV(0);
sv_setsv_nomg(sv, *relem);
*(relem++) = sv;
MEM_WRAP_CHECK_1(elem,SV*,oom_array_extend);
}
#endif
- if (!svp || *svp == &PL_sv_undef) {
+ if (!svp || !*svp) {
SV* lv;
if (!defer)
DIE(aTHX_ PL_no_aelem, elem);
/* Element [1] is reserved for the set-up swash. If already there,
* return it; if not, create it and store it there */
- if (SvROK(ary[1])) {
+ if (ary[1] && SvROK(ary[1])) {
sw = ary[1];
}
else if (si && doinit) {