assert(HvAUX(stash));
stashhek
- = HvAUX(stash)->xhv_name && HvENAME_HEK_NN(stash)
+ = HvAUX(stash)->xhv_name_u.xhvnameu_name && HvENAME_HEK_NN(stash)
? HvENAME_HEK_NN(stash)
: HvNAME_HEK(stash);
=cut
*/
+
+/* Macro to avoid repeating the code five times. */
+#define CLEAR_LINEAR(mEta) \
+ if (mEta->mro_linear_all) { \
+ SvREFCNT_dec(MUTABLE_SV(mEta->mro_linear_all)); \
+ mEta->mro_linear_all = NULL; \
+ /* This is just acting as a shortcut pointer. */ \
+ mEta->mro_linear_current = NULL; \
+ } else if (mEta->mro_linear_current) { \
+ /* Only the current MRO is stored, so this owns the data. */ \
+ SvREFCNT_dec(mEta->mro_linear_current); \
+ mEta->mro_linear_current = NULL; \
+ }
+
void
Perl_mro_isa_changed_in(pTHX_ HV* stash)
{
/* wipe out the cached linearizations for this stash */
meta = HvMROMETA(stash);
- if (meta->mro_linear_all) {
- SvREFCNT_dec(MUTABLE_SV(meta->mro_linear_all));
- meta->mro_linear_all = NULL;
- /* This is just acting as a shortcut pointer. */
- meta->mro_linear_current = NULL;
- } else if (meta->mro_linear_current) {
- /* Only the current MRO is stored, so this owns the data. */
- SvREFCNT_dec(meta->mro_linear_current);
- meta->mro_linear_current = NULL;
- }
+ CLEAR_LINEAR(meta);
if (meta->isa) {
/* Steal it for our own purposes. */
isa = (HV *)sv_2mortal((SV *)meta->isa);
if(!revstash) continue;
revmeta = HvMROMETA(revstash);
- if (revmeta->mro_linear_all) {
- SvREFCNT_dec(MUTABLE_SV(revmeta->mro_linear_all));
- revmeta->mro_linear_all = NULL;
- /* This is just acting as a shortcut pointer. */
- revmeta->mro_linear_current = NULL;
- } else if (revmeta->mro_linear_current) {
- /* Only the current MRO is stored, so this owns the data. */
- SvREFCNT_dec(revmeta->mro_linear_current);
- revmeta->mro_linear_current = NULL;
- }
+ CLEAR_LINEAR(revmeta);
if(!is_universal)
revmeta->cache_gen++;
if(revmeta->mro_nextmethod)
HV * const isa = (HV *)HeVAL(iter);
const HEK *namehek;
- /* Re-calculate the linearisation, unless a previous iter-
- ation was for a subclass of this class. */
- if(!HvMROMETA(revstash)->isa)
- (void)mro_get_linear_isa(revstash);
-
/* We're starting at the 2nd element, skipping revstash */
linear_mro = mro_get_linear_isa(revstash);
svp = AvARRAY(linear_mro) + 1;
Call this function to signal to a stash that it has been assigned to
another spot in the stash hierarchy. C<stash> is the stash that has been
assigned. C<oldstash> is the stash it replaces, if any. C<gv> is the glob
-that is actually being assigned to. C<newname> and C<newname_len> are the
-full name of the GV. If these last two arguments are omitted, they can be
-inferred from C<gv>. C<gv> can be omitted if C<newname> is given.
+that is actually being assigned to.
This can also be called with a null first argument to
indicate that C<oldstash> has been deleted.
It also sets the effective names (C<HvENAME>) on all the stashes as
appropriate.
+If the C<gv> is present and is not in the symbol table, then this function
+simply returns. This checked will be skipped if C<flags & 1>.
+
=cut
*/
void
Perl_mro_package_moved(pTHX_ HV * const stash, HV * const oldstash,
- const GV *gv, const char *newname,
- I32 newname_len)
+ const GV * const gv, U32 flags)
{
+ SV *namesv;
+ HEK **namep;
+ I32 name_count;
HV *stashes;
HE* iter;
+ PERL_ARGS_ASSERT_MRO_PACKAGE_MOVED;
assert(stash || oldstash);
- assert(gv || newname);
- /* Determine the name of the location that stash was assigned to
+ /* Determine the name(s) of the location that stash was assigned to
* or from which oldstash was removed.
*
* We cannot reliably use the name in oldstash, because it may have
* *$globref = *frelp::;
* # calls mro_package_moved(%frelp::, %baz::, *$globref, NULL, 0)
*
- * If newname is not null, then we trust that the caller gave us the
- * right name. Otherwise, we get it from the gv. But if the gv is not
- * in the symbol table, then we just return.
+ * So we get it from the gv. But, since the gv may no longer be in the
+ * symbol table, we check that first. The only reliable way to tell is
+ * to see whether its stash has an effective name and whether the gv
+ * resides in that stash under its name. That effective name may be
+ * different from what gv_fullname4 would use.
+ * If flags & 1, the caller has asked us to skip the check.
*/
- if(!newname && gv) {
- SV * const namesv = sv_newmortal();
- STRLEN len;
- gv_fullname4(namesv, gv, NULL, 0);
- if(gv_fetchsv(namesv, GV_NOADD_NOINIT, SVt_PVGV) != gv) return;
- newname = SvPV_const(namesv, len);
- newname_len = len - 2; /* skip trailing :: */
+ if(!(flags & 1)) {
+ SV **svp;
+ if(
+ !GvSTASH(gv) || !HvENAME(GvSTASH(gv)) ||
+ !(svp = hv_fetch(GvSTASH(gv), GvNAME(gv), GvNAMELEN(gv), 0)) ||
+ *svp != (SV *)gv
+ ) return;
+ }
+ assert(SvOOK(GvSTASH(gv)));
+ assert(GvNAMELEN(gv) > 1);
+ assert(GvNAME(gv)[GvNAMELEN(gv) - 1] == ':');
+ assert(GvNAME(gv)[GvNAMELEN(gv) - 2] == ':');
+ name_count = HvAUX(GvSTASH(gv))->xhv_name_count;
+ if (!name_count) {
+ name_count = 1;
+ namep = &HvAUX(GvSTASH(gv))->xhv_name_u.xhvnameu_name;
+ }
+ else {
+ namep = HvAUX(GvSTASH(gv))->xhv_name_u.xhvnameu_names;
+ if (name_count < 0) ++namep, name_count = -name_count - 1;
+ }
+ if (name_count == 1) {
+ if (HEK_LEN(*namep) == 4 && strnEQ(HEK_KEY(*namep), "main", 4)) {
+ namesv = newSVpvs_flags("", SVs_TEMP);
+ }
+ else {
+ namesv = sv_2mortal(newSVhek(*namep));
+ sv_catpvs(namesv, "::");
+ }
+ sv_catpvn(namesv, GvNAME(gv), GvNAMELEN(gv) - 2);
+ /* skip trailing :: */
+ }
+ else {
+ SV *aname;
+ namesv = sv_2mortal((SV *)newAV());
+ while (name_count--) {
+ if(HEK_LEN(*namep) == 4 && strnEQ(HEK_KEY(*namep), "main", 4)){
+ aname = newSVpvs(""); namep++;
+ }
+ else {
+ aname = newSVhek(*namep++);
+ sv_catpvs(aname, "::");
+ }
+ sv_catpvn(aname, GvNAME(gv), GvNAMELEN(gv) - 2);
+ /* skip trailing :: */
+ av_push((AV *)namesv, aname);
+ }
}
- if(newname_len < 0) newname_len = -newname_len;
/* Get a list of all the affected classes. */
/* We cannot simply pass them all to mro_isa_changed_in to avoid
as neither B nor B::B can be updated before the other, since they
will reset caches on foo, which will see either B or B::B with the
wrong name. The names must be set on *all* affected stashes before
- we do anything else.
+ we do anything else. (And linearisations must be cleared, too.)
*/
stashes = (HV *) sv_2mortal((SV *)newHV());
mro_gather_and_rename(
stashes, (HV *) sv_2mortal((SV *)newHV()),
- stash, oldstash, newname, newname_len
+ stash, oldstash, namesv
);
- /* Iterate through the stashes, wiping isa linearisations, but leaving
- the isa hash (which mro_isa_changed_in needs for adjusting the
- isarev hashes belonging to parent classes). */
- hv_iterinit(stashes);
- while((iter = hv_iternext(stashes))) {
- if(HeVAL(iter) != &PL_sv_yes && HvENAME(HeVAL(iter))) {
- struct mro_meta* meta;
- meta = HvMROMETA((HV *)HeVAL(iter));
- if (meta->mro_linear_all) {
- SvREFCNT_dec(MUTABLE_SV(meta->mro_linear_all));
- meta->mro_linear_all = NULL;
- /* This is just acting as a shortcut pointer. */
- meta->mro_linear_current = NULL;
- } else if (meta->mro_linear_current) {
- /* Only the current MRO is stored, so this owns the data. */
- SvREFCNT_dec(meta->mro_linear_current);
- meta->mro_linear_current = NULL;
- }
- }
- }
-
/* Once the caches have been wiped on all the classes, call
mro_isa_changed_in on each. */
hv_iterinit(stashes);
while((iter = hv_iternext(stashes))) {
- if(HvENAME(HeVAL(iter)))
- mro_isa_changed_in((HV *)HeVAL(iter));
+ HV * const stash = *(HV **)HEK_KEY(HeKEY_hek(iter));
+ if(HvENAME(stash)) {
+ /* We have to restore the original meta->isa (that
+ mro_gather_and_rename set aside for us) this way, in case
+ one class in this list is a superclass of a another class
+ that we have already encountered. In such a case, meta->isa
+ will have been overwritten without old entries being deleted
+ from PL_isarev. */
+ struct mro_meta * const meta = HvMROMETA(stash);
+ if(meta->isa != (HV *)HeVAL(iter)){
+ SvREFCNT_dec(meta->isa);
+ meta->isa
+ = HeVAL(iter) == &PL_sv_yes
+ ? NULL
+ : (HV *)HeVAL(iter);
+ HeVAL(iter) = NULL; /* We donated our reference count. */
+ }
+ mro_isa_changed_in(stash);
+ }
}
}
void
S_mro_gather_and_rename(pTHX_ HV * const stashes, HV * const seen_stashes,
- HV *stash, HV *oldstash, const char *name,
- I32 namlen)
+ HV *stash, HV *oldstash, SV *namesv)
{
register XPVHV* xhv;
register HE *entry;
I32 riter = -1;
+ I32 items = 0;
const bool stash_had_name = stash && HvENAME(stash);
+ bool fetched_isarev = FALSE;
HV *seen = NULL;
HV *isarev = NULL;
- SV **svp;
+ SV **svp = NULL;
PERL_ARGS_ASSERT_MRO_GATHER_AND_RENAME;
if(oldstash) {
/* Add to the big list. */
+ struct mro_meta * meta;
HE * const entry
= (HE *)
hv_common(
}
HeVAL(entry)
= HeVAL(entry) == &PL_sv_no ? &PL_sv_yes : &PL_sv_undef;
+ meta = HvMROMETA(oldstash);
(void)
hv_store(
stashes, (const char *)&oldstash, sizeof(HV *),
- SvREFCNT_inc_simple_NN((SV*)oldstash), 0
+ meta->isa
+ ? SvREFCNT_inc_simple_NN((SV *)meta->isa)
+ : &PL_sv_yes,
+ 0
);
+ CLEAR_LINEAR(meta);
/* Update the effective name. */
if(HvENAME_get(oldstash)) {
- const HEK * const enamehek = HvENAME_HEK(oldstash);
- if(PL_stashcache)
- (void)
- hv_delete(PL_stashcache, name, namlen, G_DISCARD);
- hv_ename_delete(oldstash, name, namlen);
-
- /* If the name deletion caused a name change, then we are not
- * going to call mro_isa_changed_in with this name (and not at all
- * if it has become anonymous) so we need to delete old isarev
- * entries here, both those in the superclasses and this class’s
- * own list of subclasses. We simply delete the latter from
- * from PL_isarev, since we still need it. hv_delete mortifies it
- * for us, so sv_2mortal is not necessary. */
- if(HvENAME_HEK(oldstash) != enamehek) {
- const struct mro_meta * meta = HvMROMETA(oldstash);
- if(meta->isa && HvARRAY(meta->isa))
- mro_clean_isarev(meta->isa, name, namlen, NULL);
- isarev = (HV *)hv_delete(PL_isarev, name, namlen, 0);
- }
+ const HEK * const enamehek = HvENAME_HEK(oldstash);
+ if(SvTYPE(namesv) == SVt_PVAV) {
+ items = AvFILLp((AV *)namesv) + 1;
+ svp = AvARRAY((AV *)namesv);
+ }
+ else {
+ items = 1;
+ svp = &namesv;
+ }
+ while (items--) {
+ STRLEN len;
+ const char *name = SvPVx_const(*svp++, len);
+ if(PL_stashcache)
+ (void)hv_delete(PL_stashcache, name, len, G_DISCARD);
+ hv_ename_delete(oldstash, name, len, 0);
+
+ if (!fetched_isarev) {
+ /* If the name deletion caused a name change, then we
+ * are not going to call mro_isa_changed_in with this
+ * name (and not at all if it has become anonymous) so
+ * we need to delete old isarev entries here, both
+ * those in the superclasses and this class’s own list
+ * of subclasses. We simply delete the latter from
+ * PL_isarev, since we still need it. hv_delete morti-
+ * fies it for us, so sv_2mortal is not necessary. */
+ if(HvENAME_HEK(oldstash) != enamehek) {
+ if(meta->isa && HvARRAY(meta->isa))
+ mro_clean_isarev(meta->isa, name, len, NULL);
+ isarev = (HV *)hv_delete(PL_isarev, name, len, 0);
+ fetched_isarev=TRUE;
+ }
+ }
+ }
}
}
check_stash:
if(stash) {
- hv_ename_add(stash, name, namlen);
+ if(SvTYPE(namesv) == SVt_PVAV) {
+ items = AvFILLp((AV *)namesv) + 1;
+ svp = AvARRAY((AV *)namesv);
+ }
+ else {
+ items = 1;
+ svp = &namesv;
+ }
+ while (items--) {
+ STRLEN len;
+ const char *name = SvPVx_const(*svp++, len);
+ hv_ename_add(stash, name, len, 0);
+ }
/* Add it to the big list if it needs
* mro_isa_changed_in called on it. That happens if it was
* detached from the symbol table (so it had no HvENAME) before
* being assigned to the spot named by the ‘name’ variable, because
- * its cached isa linerisation is now stale (the effective name
+ * its cached isa linearisation is now stale (the effective name
* having changed), and subclasses will then use that cache when
* mro_package_moved calls mro_isa_changed_in. (See
* [perl #77358].)
HeVAL(entry)
= HeVAL(entry) == &PL_sv_undef ? &PL_sv_yes : &PL_sv_no;
if(!stash_had_name)
+ {
+ struct mro_meta * const meta = HvMROMETA(stash);
(void)
hv_store(
stashes, (const char *)&stash, sizeof(HV *),
- SvREFCNT_inc_simple_NN((SV *)stash), 0
+ meta->isa
+ ? SvREFCNT_inc_simple_NN((SV *)meta->isa)
+ : &PL_sv_yes,
+ 0
);
+ CLEAR_LINEAR(meta);
+ }
}
}
return;
/* Add all the subclasses to the big list. */
+ if(!fetched_isarev) {
+ /* If oldstash is not null, then we can use its HvENAME to look up
+ the isarev hash, since all its subclasses will be listed there.
+ It will always have an HvENAME. It the HvENAME was removed
+ above, then fetch_isarev will be true, and this code will not be
+ reached.
+
+ If oldstash is null, then this is an empty spot with no stash in
+ it, so subclasses could be listed in isarev hashes belonging to
+ any of the names, so we have to check all of them.
+ */
+ assert(!oldstash || HvENAME(oldstash));
+ if (oldstash) {
+ /* Extra variable to avoid a compiler warning */
+ char * const hvename = HvENAME(oldstash);
+ fetched_isarev = TRUE;
+ svp = hv_fetch(PL_isarev, hvename, HvENAMELEN_get(oldstash), 0);
+ if (svp) isarev = MUTABLE_HV(*svp);
+ }
+ else if(SvTYPE(namesv) == SVt_PVAV) {
+ items = AvFILLp((AV *)namesv) + 1;
+ svp = AvARRAY((AV *)namesv);
+ }
+ else {
+ items = 1;
+ svp = &namesv;
+ }
+ }
if(
- isarev
- || (
- (svp = hv_fetch(PL_isarev, name, namlen, 0))
- && (isarev = MUTABLE_HV(*svp))
- )
+ isarev || !fetched_isarev
) {
+ while (fetched_isarev || items--) {
HE *iter;
+
+ if (!fetched_isarev) {
+ HE * const he = hv_fetch_ent(PL_isarev, *svp++, 0, 0);
+ if (!he || !(isarev = MUTABLE_HV(HeVAL(he)))) continue;
+ }
+
hv_iterinit(isarev);
while((iter = hv_iternext(isarev))) {
I32 len;
const char* const revkey = hv_iterkey(iter, &len);
HV* revstash = gv_stashpvn(revkey, len, 0);
+ struct mro_meta * meta;
if(!revstash) continue;
+ meta = HvMROMETA(revstash);
(void)
hv_store(
stashes, (const char *)&revstash, sizeof(HV *),
- SvREFCNT_inc_simple_NN((SV *)revstash), 0
+ meta->isa
+ ? SvREFCNT_inc_simple_NN((SV *)meta->isa)
+ : &PL_sv_yes,
+ 0
);
+ CLEAR_LINEAR(meta);
}
- }
- if(
- (!stash || !HvARRAY(stash)) && (!oldstash || !HvARRAY(oldstash))
- ) return;
+ if (fetched_isarev) break;
+ }
+ }
/* This is partly based on code in hv_iternext_flags. We are not call-
ing that here, as we want to avoid resetting the hash iterator. */
)
{
/* Add :: and the key (minus the trailing ::)
- to newname. */
- SV *namesv
- = newSVpvn_flags(name, namlen, SVs_TEMP);
- {
- const char *name;
- STRLEN namlen;
- sv_catpvs(namesv, "::");
- sv_catpvn(namesv, key, len-2);
- name = SvPV_const(namesv, namlen);
- mro_gather_and_rename(
- stashes, seen_stashes,
- substash, oldsubstash, name, namlen
- );
+ to each name. */
+ SV *subname;
+ if(SvTYPE(namesv) == SVt_PVAV) {
+ SV *aname;
+ items = AvFILLp((AV *)namesv) + 1;
+ svp = AvARRAY((AV *)namesv);
+ subname = sv_2mortal((SV *)newAV());
+ while (items--) {
+ aname = newSVsv(*svp++);
+ sv_catpvs(aname, "::");
+ sv_catpvn(aname, key, len-2);
+ av_push((AV *)subname, aname);
+ }
+ }
+ else {
+ subname = sv_2mortal(newSVsv(namesv));
+ sv_catpvs(subname, "::");
+ sv_catpvn(subname, key, len-2);
}
+ mro_gather_and_rename(
+ stashes, seen_stashes,
+ substash, oldsubstash, subname
+ );
}
(void)hv_store(seen, key, len, &PL_sv_yes, 0);
substash = GvHV(HeVAL(entry));
if(substash) {
- SV *namesv;
- const char *subname;
- STRLEN subnamlen;
+ SV *subname;
/* Avoid checking main::main::main::... */
if(substash == stash) continue;
/* Add :: and the key (minus the trailing ::)
- to newname. */
- namesv
- = newSVpvn_flags(name, namlen, SVs_TEMP);
- sv_catpvs(namesv, "::");
- sv_catpvn(namesv, key, len-2);
- subname = SvPV_const(namesv, subnamlen);
+ to each name. */
+ if(SvTYPE(namesv) == SVt_PVAV) {
+ SV *aname;
+ items = AvFILLp((AV *)namesv) + 1;
+ svp = AvARRAY((AV *)namesv);
+ subname = sv_2mortal((SV *)newAV());
+ while (items--) {
+ aname = newSVsv(*svp++);
+ sv_catpvs(aname, "::");
+ sv_catpvn(aname, key, len-2);
+ av_push((AV *)subname, aname);
+ }
+ }
+ else {
+ subname = sv_2mortal(newSVsv(namesv));
+ sv_catpvs(subname, "::");
+ sv_catpvn(subname, key, len-2);
+ }
mro_gather_and_rename(
stashes, seen_stashes,
- substash, NULL, subname, subnamlen
+ substash, NULL, subname
);
}
}