Apo |void |hv_riter_set |NN HV *hv|I32 riter
Apo |void |hv_eiter_set |NN HV *hv|NULLOK HE *eiter
Ap |void |hv_name_set |NN HV *hv|NULLOK const char *name|U32 len|U32 flags
-p |void |hv_name_add |NN HV *hv|NN const char *name|U32 len
-p |void |hv_name_delete |NN HV *hv|NN const char *name|U32 len
+p |void |hv_ename_add |NN HV *hv|NN const char *name|U32 len
+p |void |hv_ename_delete|NN HV *hv|NN const char *name|U32 len
: Used in dump.c and hv.c
poM |AV** |hv_backreferences_p |NN HV *hv
#if defined(PERL_IN_DUMP_C) || defined(PERL_IN_HV_C) || defined(PERL_IN_SV_C)
#define get_no_modify() Perl_get_no_modify(aTHX)
#define get_opargs() Perl_get_opargs(aTHX)
#define gv_try_downgrade(a) Perl_gv_try_downgrade(aTHX_ a)
-#define hv_name_add(a,b,c) Perl_hv_name_add(aTHX_ a,b,c)
-#define hv_name_delete(a,b,c) Perl_hv_name_delete(aTHX_ a,b,c)
+#define hv_ename_add(a,b,c) Perl_hv_ename_add(aTHX_ a,b,c)
+#define hv_ename_delete(a,b,c) Perl_hv_ename_delete(aTHX_ a,b,c)
#define init_argv_symbols(a,b) Perl_init_argv_symbols(aTHX_ a,b)
#define init_debugger() Perl_init_debugger(aTHX)
#define intro_my() Perl_intro_my(aTHX)
actually detached from the hash, as mro_package_moved checks
whether the passed gv is still in the symbol table before
doing anything. */
- if (HeVAL(entry) && HvNAME(hv)) {
+ if (HeVAL(entry) && HvENAME_get(hv)) {
if (keysv) key = SvPV(keysv, klen);
if (klen > 1 && key[klen-2] == ':' && key[klen-1] == ':'
&& (klen != 6 || hv!=PL_defstash || memNE(key,"main::",6))
&& SvTYPE(HeVAL(entry)) == SVt_PVGV) {
HV * const stash = GvHV((GV *)HeVAL(entry));
- if (stash && HvNAME(stash))
+ if (stash && HvENAME_get(stash))
mro_package_moved(
NULL, stash, (GV *)HeVAL(entry), NULL, 0
);
/* This is the array that we're going to restore */
HE **const orig_array = HvARRAY(hv);
HEK *name;
- U32 name_count;
+ I32 name_count;
int attempts = 100;
PERL_ARGS_ASSERT_HFREEENTRIES;
if (HvAUX(hv)->xhv_name) {
if(HvAUX(hv)->xhv_name_count) {
HEK ** const name = (HEK **)HvAUX(hv)->xhv_name;
- HEK **hekp = name + HvAUX(hv)->xhv_name_count;
+ I32 const count = HvAUX(hv)->xhv_name_count;
+ HEK **hekp = name + (count < 0 ? -count : count);
while(hekp-- > name)
unshare_hek_or_pvn(*hekp, 0, 0, 0);
Safefree(name);
dVAR;
struct xpvhv_aux *iter;
U32 hash;
+ HEK **spot;
PERL_ARGS_ASSERT_HV_NAME_SET;
PERL_UNUSED_ARG(flags);
iter = HvAUX(hv);
if (iter->xhv_name) {
if(iter->xhv_name_count) {
+ if(!name) {
HEK ** const name = (HEK **)HvAUX(hv)->xhv_name;
- HEK **hekp = name + HvAUX(hv)->xhv_name_count;
- while(hekp-- > name)
+ HEK **hekp = name + (
+ iter->xhv_name_count < 0
+ ? -iter->xhv_name_count
+ : iter->xhv_name_count
+ );
+ while(hekp-- > name+1)
unshare_hek_or_pvn(*hekp, 0, 0, 0);
+ /* The first elem may be null. */
+ if(*name) unshare_hek_or_pvn(*name, 0, 0, 0);
Safefree(name);
+ spot = &iter->xhv_name;
+ iter->xhv_name_count = 0;
+ }
+ else {
+ spot = (HEK **)iter->xhv_name;
+ if(iter->xhv_name_count > 0) {
+ /* shift some things over */
+ Renew(spot, iter->xhv_name_count, HEK *);
+ spot[iter->xhv_name_count++] = spot[1];
+ spot[1] = spot[0];
+ }
+ else if(*spot) {
+ unshare_hek_or_pvn(*spot, 0, 0, 0);
+ }
+ }
+ }
+ else {
+ unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0);
+ spot = &iter->xhv_name;
}
- else unshare_hek_or_pvn(iter->xhv_name, 0, 0, 0);
}
+ else spot = &iter->xhv_name;
} else {
if (name == 0)
return;
iter = hv_auxinit(hv);
+ spot = &iter->xhv_name;
}
PERL_HASH(hash, name, len);
- iter->xhv_name = name ? share_hek(name, len, hash) : NULL;
+ *spot = name ? share_hek(name, len, hash) : NULL;
iter->xhv_name_count = 0;
}
void
-Perl_hv_name_add(pTHX_ HV *hv, const char *name, U32 len)
+Perl_hv_ename_add(pTHX_ HV *hv, const char *name, U32 len)
{
dVAR;
struct xpvhv_aux *aux = SvOOK(hv) ? HvAUX(hv) : hv_auxinit(hv);
U32 hash;
- PERL_ARGS_ASSERT_HV_NAME_ADD;
+ PERL_ARGS_ASSERT_HV_ENAME_ADD;
if (len > I32_MAX)
Perl_croak(aTHX_ "panic: hv name too long (%"UVuf")", (UV) len);
PERL_HASH(hash, name, len);
- if (!aux->xhv_name) {
- aux->xhv_name = share_hek(name, len, hash);
- return;
- }
-
if (aux->xhv_name_count) {
HEK ** const xhv_name = (HEK **)aux->xhv_name;
- HEK **hekp = xhv_name + aux->xhv_name_count;
- U32 count = aux->xhv_name_count;
+ I32 count = aux->xhv_name_count;
+ HEK **hekp = xhv_name + (count < 0 ? -count : count);
while (hekp-- > xhv_name)
if (
HEK_LEN(*hekp) == (I32)len && memEQ(HEK_KEY(*hekp), name, len)
- ) return;
- aux->xhv_name_count++;
- Renewc(aux->xhv_name, aux->xhv_name_count, HEK *, HEK);
+ ) {
+ if (hekp == xhv_name && count < 0)
+ aux->xhv_name_count = -count;
+ return;
+ }
+ if (count < 0) aux->xhv_name_count--, count = -count;
+ else aux->xhv_name_count++;
+ Renewc(aux->xhv_name, count + 1, HEK *, HEK);
((HEK **)aux->xhv_name)[count] = share_hek(name, len, hash);
}
else {
HEK *existing_name = aux->xhv_name;
if (
- HEK_LEN(existing_name) == (I32)len
+ existing_name && HEK_LEN(existing_name) == (I32)len
&& memEQ(HEK_KEY(existing_name), name, len)
) return;
Newxc(aux->xhv_name, 2, HEK *, HEK);
- aux->xhv_name_count = 2;
+ aux->xhv_name_count = existing_name ? 2 : -2;
*(HEK **)aux->xhv_name = existing_name;
((HEK **)aux->xhv_name)[1] = share_hek(name, len, hash);
}
}
void
-Perl_hv_name_delete(pTHX_ HV *hv, const char *name, U32 len)
+Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len)
{
dVAR;
struct xpvhv_aux *aux;
- PERL_ARGS_ASSERT_HV_NAME_DELETE;
+ PERL_ARGS_ASSERT_HV_ENAME_DELETE;
if (len > I32_MAX)
Perl_croak(aTHX_ "panic: hv name too long (%"UVuf")", (UV) len);
if (aux->xhv_name_count) {
HEK ** const namep = (HEK **)aux->xhv_name;
- HEK **victim = namep + aux->xhv_name_count;
- while (victim-- > namep)
+ I32 const count = aux->xhv_name_count;
+ HEK **victim = namep + (count < 0 ? -count : count);
+ while (victim-- > namep + 1)
if (
HEK_LEN(*victim) == (I32)len
&& memEQ(HEK_KEY(*victim), name, len)
) {
unshare_hek_or_pvn(*victim, 0, 0, 0);
- if (!--aux->xhv_name_count) { /* none left */
+ if (count < 0) ++aux->xhv_name_count;
+ else --aux->xhv_name_count;
+ if (
+ (aux->xhv_name_count == 1 || aux->xhv_name_count == -1)
+ && !*namep
+ ) { /* if there are none left */
Safefree(namep);
aux->xhv_name = NULL;
+ aux->xhv_name_count = 0;
}
else {
/* Move the last one back to fill the empty slot. It
does not matter what order they are in. */
- *victim = *(namep + aux->xhv_name_count);
+ *victim = *(namep + (count < 0 ? -count : count) - 1);
}
return;
}
+ if (
+ count > 0 && HEK_LEN(*namep) == (I32)len
+ && memEQ(HEK_KEY(*namep),name,len)
+ ) {
+ aux->xhv_name_count = -count;
+ }
}
else if(
HEK_LEN(aux->xhv_name) == (I32)len
HE *xhv_eiter; /* current entry of iterator */
I32 xhv_riter; /* current root of iterator */
struct mro_meta *xhv_mro_meta;
- U32 xhv_name_count; /* When non-zero, xhv_name is actually */
- /* a pointer to an array of HEKs, this */
-}; /* being the length. */
+/* Concerning xhv_name_count: When non-zero, xhv_name is actually a pointer
+ * to an array of HEK pointers, this being the length. The first element is
+ * the name of the stash, which may be NULL. If xhv_name_count is positive,
+ * then *xhv_name is one of the effective names. If xhv_name_count is nega-
+ * tive, then xhv_name[1] is the first effective name.
+ */
+ I32 xhv_name_count;
+};
/* hash structure: */
/* This structure must match the beginning of struct xpvmg in sv.h. */
/* This macro may go away without notice. */
#define HvNAME_HEK(hv) \
(SvOOK(hv) && HvAUX(hv)->xhv_name ? HvNAME_HEK_NN(hv) : NULL)
-#define HvNAME_get(hv) ((SvOOK(hv) && (HvAUX(hv)->xhv_name)) \
+#define HvNAME_get(hv) \
+ ((SvOOK(hv) && (HvAUX(hv)->xhv_name) && HvNAME_HEK_NN(hv)) \
? HEK_KEY(HvNAME_HEK_NN(hv)) : NULL)
-#define HvNAMELEN_get(hv) ((SvOOK(hv) && (HvAUX(hv)->xhv_name)) \
+#define HvNAMELEN_get(hv) \
+ ((SvOOK(hv) && (HvAUX(hv)->xhv_name) && HvNAME_HEK_NN(hv)) \
? HEK_LEN(HvNAME_HEK_NN(hv)) : 0)
+#ifdef PERL_CORE
+# define HvENAME_HEK_NN(hv) \
+ ( \
+ HvAUX(hv)->xhv_name_count > 0 ? *(HEK **)HvAUX(hv)->xhv_name : \
+ HvAUX(hv)->xhv_name_count < -1 ? ((HEK **)HvAUX(hv)->xhv_name)[1] : \
+ HvAUX(hv)->xhv_name_count == -1 ? NULL : \
+ HvAUX(hv)->xhv_name \
+ )
+# define HvENAME_get(hv) \
+ ((SvOOK(hv) && (HvAUX(hv)->xhv_name) && HvENAME_HEK_NN(hv)) \
+ ? HEK_KEY(HvENAME_HEK_NN(hv)) : NULL)
+# define HvENAMELEN_get(hv) \
+ ((SvOOK(hv) && (HvAUX(hv)->xhv_name) && HvENAME_HEK_NN(hv)) \
+ ? HEK_LEN(HvENAME_HEK_NN(hv)) : 0)
+#endif
/* the number of keys (including any placeholers) */
#define XHvTOTALKEYS(xhv) ((xhv)->xhv_keys)
if(PL_stashcache)
(void)
hv_delete(PL_stashcache, newname, newname_len, G_DISCARD);
- hv_name_delete(oldstash, newname, newname_len);
+ hv_ename_delete(oldstash, newname, newname_len);
}
- if(stash) hv_name_add(stash, newname, newname_len);
+ if(stash) hv_ename_add(stash, newname, newname_len);
}
/*
#define PERL_ARGS_ASSERT_HV_EITER_SET \
assert(hv)
+PERL_CALLCONV void Perl_hv_ename_add(pTHX_ HV *hv, const char *name, U32 len)
+ __attribute__nonnull__(pTHX_1)
+ __attribute__nonnull__(pTHX_2);
+#define PERL_ARGS_ASSERT_HV_ENAME_ADD \
+ assert(hv); assert(name)
+
+PERL_CALLCONV void Perl_hv_ename_delete(pTHX_ HV *hv, const char *name, U32 len)
+ __attribute__nonnull__(pTHX_1)
+ __attribute__nonnull__(pTHX_2);
+#define PERL_ARGS_ASSERT_HV_ENAME_DELETE \
+ assert(hv); assert(name)
+
/* PERL_CALLCONV bool Perl_hv_exists(pTHX_ HV *hv, const char *key, I32 klen)
__attribute__warn_unused_result__
__attribute__nonnull__(pTHX_2); */
#define PERL_ARGS_ASSERT_HV_MAGIC \
assert(hv)
-PERL_CALLCONV void Perl_hv_name_add(pTHX_ HV *hv, const char *name, U32 len)
- __attribute__nonnull__(pTHX_1)
- __attribute__nonnull__(pTHX_2);
-#define PERL_ARGS_ASSERT_HV_NAME_ADD \
- assert(hv); assert(name)
-
-PERL_CALLCONV void Perl_hv_name_delete(pTHX_ HV *hv, const char *name, U32 len)
- __attribute__nonnull__(pTHX_1)
- __attribute__nonnull__(pTHX_2);
-#define PERL_ARGS_ASSERT_HV_NAME_DELETE \
- assert(hv); assert(name)
-
PERL_CALLCONV void Perl_hv_name_set(pTHX_ HV *hv, const char *name, U32 len, U32 flags)
__attribute__nonnull__(pTHX_1);
#define PERL_ARGS_ASSERT_HV_NAME_SET \
if(mro_changes == 2) mro_isa_changed_in(GvSTASH(dstr));
else if(mro_changes == 3) {
HV * const stash = GvHV(dstr);
- if(old_stash ? (HV *)HvNAME(old_stash) : stash)
+ if(old_stash ? (HV *)HvENAME_get(old_stash) : stash)
mro_package_moved(
stash, old_stash,
(GV *)dstr, NULL, 0
const STRLEN len = GvNAMELEN(dstr);
if (
len > 1 && name[len-2] == ':' && name[len-1] == ':'
- && (!dref || HvNAME(dref))
+ && (!dref || HvENAME_get(dref))
) {
mro_package_moved(
(HV *)sref, (HV *)dref,
if (reset_isa) {
HV * const stash = GvHV(dstr);
if(
- old_stash ? (HV *)HvNAME(old_stash) : stash
+ old_stash ? (HV *)HvENAME_get(old_stash) : stash
)
mro_package_moved(
stash, old_stash,
hvname = saux->xhv_name;
if (saux->xhv_name_count) {
HEK ** const sname = (HEK **)saux->xhv_name;
- const U32 count = saux->xhv_name_count;
+ const I32 count
+ = saux->xhv_name_count < 0
+ ? -saux->xhv_name_count
+ : saux->xhv_name_count;
HEK **shekp = sname + count;
HEK **dhekp;
Newxc(daux->xhv_name, count, HEK *, HEK);
BEGIN { require "./test.pl"; }
-plan( tests => 38 );
+plan( tests => 46 );
# Used to segfault (bug #15479)
fresh_perl_like(
__ANON__();
is ($c, 'main::__ANON__', '__ANON__ sub called ok');
}
+
+# Stashes that are effectively renamed
+{
+ package rile;
+
+ my $obj = bless [];
+ my $globref = \*tat;
+
+ # effectively rename a stash
+ *slin:: = *rile::; *rile:: = *zor::;
+
+ ::is *$globref, "*rile::tat",
+ 'globs stringify the same way when stashes are moved';
+ ::is ref $obj, "rile",
+ 'ref() returns the same thing when an object’s stash is moved';
+ ::like "$obj", qr "^rile=ARRAY\(0x[\da-f]+\)\z",
+ 'objects stringify the same way when their stashes are moved';
+ ::is eval '__PACKAGE__', 'rile',
+ '__PACKAGE__ returns the same thing when the current stash is moved';
+
+ # Now detach it completely from the symtab, making it effect-
+ # ively anonymous
+ my $life_raft = \%slin::;
+ *slin:: = *zor::;
+
+ ::is *$globref, "*rile::tat",
+ 'globs stringify the same way when stashes are detached';
+ ::is ref $obj, "rile",
+ 'ref() returns the same thing when an object’s stash is detached';
+ ::like "$obj", qr "^rile=ARRAY\(0x[\da-f]+\)\z",
+ 'objects stringify the same way when their stashes are detached';
+ ::is eval '__PACKAGE__', 'rile',
+ '__PACKAGE__ returns the same when the current stash is detached';
+}
+
+