/* mro.c
*
* Copyright (c) 2007 Brandon L Black
- * Copyright (c) 2007, 2008 Larry Wall and others
+ * Copyright (c) 2007, 2008, 2009, 2010, 2011 Larry Wall and others
*
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
return INT2PTR(const struct mro_alg *, SvUVX(*data));
}
+/*
+=for apidoc mro_register
+Registers a custom mro plugin. See L<perlmroapi> for details.
+
+=cut
+*/
+
void
Perl_mro_register(pTHX_ const struct mro_alg *mro) {
SV *wrapper = newSVuv(PTR2UV(mro));
Perl_croak(aTHX_ "Can't linearize anonymous symbol table");
if (level > 100)
- Perl_croak(aTHX_ "Recursive inheritance detected in package '%s'",
- HEK_KEY(stashhek));
+ Perl_croak(aTHX_
+ "Recursive inheritance detected in package '%"HEKf"'",
+ HEKfARG(stashhek));
meta = HvMROMETA(stash);
sv_upgrade(val, SVt_PV);
SvPV_set(val, HEK_KEY(share_hek_hek(key)));
SvCUR_set(val, HEK_LEN(key));
- SvREADONLY_on(val);
- SvFAKE_on(val);
+ SvIsCOW_on(val);
SvPOK_on(val);
if (HEK_UTF8(key))
SvUTF8_on(val);
Perl_croak(aTHX_ "panic: invalid MRO!");
isa = meta->mro_which->resolve(aTHX_ stash, 0);
+ if (meta->mro_which != &dfs_alg) { /* skip for dfs, for speed */
+ SV * const namesv =
+ (HvENAME(stash)||HvNAME(stash))
+ ? newSVhek(HvENAME_HEK(stash)
+ ? HvENAME_HEK(stash)
+ : HvNAME_HEK(stash))
+ : NULL;
+
+ if(namesv && (AvFILLp(isa) == -1 || !sv_eq(*AvARRAY(isa), namesv)))
+ {
+ AV * const old = isa;
+ SV **svp;
+ SV **ovp = AvARRAY(old);
+ SV * const * const oend = ovp + AvFILLp(old) + 1;
+ isa = (AV *)sv_2mortal((SV *)newAV());
+ av_extend(isa, AvFILLp(isa) = AvFILLp(old)+1);
+ *AvARRAY(isa) = namesv;
+ svp = AvARRAY(isa)+1;
+ while (ovp < oend) *svp++ = SvREFCNT_inc(*ovp++);
+ }
+ else SvREFCNT_dec(namesv);
+ }
+
if (!meta->isa) {
HV *const isa_hash = newHV();
/* Linearisation didn't build it for us, so do it here. */
is UNIVERSAL or one of its parents */
svp = hv_fetch(PL_isarev, stashname,
- stashname_utf8 ? -stashname_len : stashname_len, 0);
+ stashname_utf8 ? -(I32)stashname_len : (I32)stashname_len, 0);
isarev = svp ? MUTABLE_HV(*svp) : NULL;
if((stashname_len == 9 && strEQ(stashname, "UNIVERSAL"))
/* wipe next::method cache too */
if(meta->mro_nextmethod) hv_clear(meta->mro_nextmethod);
+ /* Changes to @ISA might turn overloading on */
+ HvAMAGIC_on(stash);
+
+ /* DESTROY can be cached in SvSTASH. */
+ if (!SvOBJECT(stash)) SvSTASH(stash) = NULL;
+
/* Iterate the isarev (classes that are our children),
wiping out their linearization, method and isa caches
and upating PL_isarev. */
/* We have to iterate through isarev twice to avoid a chicken and
* egg problem: if A inherits from B and both are in isarev, A might
- * be processed before B and use B’s previous linearisation.
+ * be processed before B and use B's previous linearisation.
*/
/* First iteration: Wipe everything, but stash away the isa hashes
revmeta->cache_gen++;
if(revmeta->mro_nextmethod)
hv_clear(revmeta->mro_nextmethod);
+ if (!SvOBJECT(revstash)) SvSTASH(revstash) = NULL;
(void)
hv_store(
case where it doesn't exist. */
(void)hv_store(mroisarev, stashname,
- stashname_utf8 ? -stashname_len : stashname_len, &PL_sv_yes, 0);
+ stashname_utf8 ? -(I32)stashname_len : (I32)stashname_len, &PL_sv_yes, 0);
}
- /* Delete our name from our former parents’ isarevs. */
+ /* Delete our name from our former parents' isarevs. */
if(isa && HvARRAY(isa))
mro_clean_isarev(isa, stashname, stashname_len, meta->isa,
(stashname_utf8 ? SVf_UTF8 : 0) );
PERL_ARGS_ASSERT_MRO_CLEAN_ISAREV;
- /* Delete our name from our former parents’ isarevs. */
+ /* Delete our name from our former parents' isarevs. */
if(isa && HvARRAY(isa) && hv_iterinit(isa)) {
SV **svp;
while((iter = hv_iternext(isa))) {
svp = hv_fetch(PL_isarev, key, HeKUTF8(iter) ? -klen : klen, 0);
if(svp) {
HV * const isarev = (HV *)*svp;
- (void)hv_delete(isarev, name, (flags & SVf_UTF8) ? -len : len, G_DISCARD);
+ (void)hv_delete(isarev, name, (flags & SVf_UTF8) ? -(I32)len : (I32)len, G_DISCARD);
if(!HvARRAY(isarev) || !HvUSEDKEYS(isarev))
(void)hv_delete(PL_isarev, key,
HeKUTF8(iter) ? -klen : klen, G_DISCARD);
=for apidoc mro_package_moved
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
+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.
This can also be called with a null first argument to
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>.
+simply returns. This checked will be skipped if C<flags & 1>.
=cut
*/
else sv_catpvs(namesv, "::");
}
if (GvNAMELEN(gv) != 1) {
- sv_catpvn(namesv, GvNAME(gv), GvNAMELEN(gv) - 2);
+ sv_catpvn_flags(
+ namesv, GvNAME(gv), GvNAMELEN(gv) - 2,
/* skip trailing :: */
- if ( GvNAMEUTF8(gv) )
- SvUTF8_on(namesv);
+ GvNAMEUTF8(gv) ? SV_CATUTF8 : SV_CATBYTES
+ );
}
}
else {
else sv_catpvs(aname, "::");
}
if (GvNAMELEN(gv) != 1) {
- sv_catpvn(aname, GvNAME(gv), GvNAMELEN(gv) - 2);
+ sv_catpvn_flags(
+ aname, GvNAME(gv), GvNAMELEN(gv) - 2,
/* skip trailing :: */
- if ( GvNAMEUTF8(gv) )
- SvUTF8_on(aname);
+ GvNAMEUTF8(gv) ? SV_CATUTF8 : SV_CATBYTES
+ );
}
av_push((AV *)namesv, aname);
}
}
}
-void
+STATIC void
S_mro_gather_and_rename(pTHX_ HV * const stashes, HV * const seen_stashes,
HV *stash, HV *oldstash, SV *namesv)
{
- register XPVHV* xhv;
- register HE *entry;
+ XPVHV* xhv;
+ HE *entry;
I32 riter = -1;
I32 items = 0;
const bool stash_had_name = stash && HvENAME(stash);
while (items--) {
const U32 name_utf8 = SvUTF8(*svp);
STRLEN len;
- const char *name = SvPVx_const(*svp++, len);
- if(PL_stashcache)
- (void)hv_delete(PL_stashcache, name, name_utf8 ? -len : len, G_DISCARD);
+ const char *name = SvPVx_const(*svp, len);
+ if(PL_stashcache) {
+ DEBUG_o(Perl_deb(aTHX_ "mro_gather_and_rename clearing PL_stashcache for '%"SVf"'\n",
+ *svp));
+ (void)hv_delete(PL_stashcache, name, name_utf8 ? -(I32)len : (I32)len, G_DISCARD);
+ }
+ ++svp;
hv_ename_delete(oldstash, name, len, name_utf8);
if (!fetched_isarev) {
* 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
+ * 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(meta->isa && HvARRAY(meta->isa))
mro_clean_isarev(meta->isa, name, len, 0, name_utf8);
isarev = (HV *)hv_delete(PL_isarev, name,
- name_utf8 ? -len : len, 0);
+ name_utf8 ? -(I32)len : (I32)len, 0);
fetched_isarev=TRUE;
}
}
/* 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
+ * being assigned to the spot named by the 'name' variable, because
* 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
/* Iterate through the entries in this list */
for(; entry; entry = HeNEXT(entry)) {
- SV* keysv;
const char* key;
- STRLEN len;
+ I32 len;
/* If this entry is not a glob, ignore it.
Try the next. */
if (!isGV(HeVAL(entry))) continue;
- keysv = hv_iterkeysv(entry);
- key = SvPV_const(keysv, len);
+ key = hv_iterkey(entry, &len);
if ((len > 1 && key[len-2] == ':' && key[len-1] == ':')
|| (len == 1 && key[0] == ':')) {
HV * const oldsubstash = GvHV(HeVAL(entry));
SV ** const stashentry
- = stash ? hv_fetch(stash, key, SvUTF8(keysv) ? -len : len, 0) : NULL;
+ = stash ? hv_fetch(stash, key, HeUTF8(entry) ? -(I32)len : (I32)len, 0) : NULL;
HV *substash = NULL;
/* Avoid main::main::main::... */
sv_catpvs(aname, ":");
else {
sv_catpvs(aname, "::");
- sv_catpvn(aname, key, len-2);
- if ( SvUTF8(keysv) )
- SvUTF8_on(aname);
+ sv_catpvn_flags(
+ aname, key, len-2,
+ HeUTF8(entry)
+ ? SV_CATUTF8 : SV_CATBYTES
+ );
}
av_push((AV *)subname, aname);
}
if (len == 1) sv_catpvs(subname, ":");
else {
sv_catpvs(subname, "::");
- sv_catpvn(subname, key, len-2);
- if ( SvUTF8(keysv) )
- SvUTF8_on(subname);
+ sv_catpvn_flags(
+ subname, key, len-2,
+ HeUTF8(entry) ? SV_CATUTF8 : SV_CATBYTES
+ );
}
}
mro_gather_and_rename(
);
}
- (void)hv_store(seen, key, SvUTF8(keysv) ? -len : len, &PL_sv_yes, 0);
+ (void)hv_store(seen, key, HeUTF8(entry) ? -(I32)len : (I32)len, &PL_sv_yes, 0);
}
}
}
/* Iterate through the entries in this list */
for(; entry; entry = HeNEXT(entry)) {
- SV* keysv;
const char* key;
- STRLEN len;
+ I32 len;
/* If this entry is not a glob, ignore it.
Try the next. */
if (!isGV(HeVAL(entry))) continue;
- keysv = hv_iterkeysv(entry);
- key = SvPV_const(keysv, len);
+ key = hv_iterkey(entry, &len);
if ((len > 1 && key[len-2] == ':' && key[len-1] == ':')
|| (len == 1 && key[0] == ':')) {
HV *substash;
/* If this entry was seen when we iterated through the
oldstash, skip it. */
- if(seen && hv_exists(seen, key, SvUTF8(keysv) ? -len : len)) continue;
+ if(seen && hv_exists(seen, key, HeUTF8(entry) ? -(I32)len : (I32)len)) continue;
/* We get here only if this stash has no corresponding
entry in the stash being replaced. */
sv_catpvs(aname, ":");
else {
sv_catpvs(aname, "::");
- sv_catpvn(aname, key, len-2);
- if ( SvUTF8(keysv) )
- SvUTF8_on(aname);
+ sv_catpvn_flags(
+ aname, key, len-2,
+ HeUTF8(entry)
+ ? SV_CATUTF8 : SV_CATBYTES
+ );
}
av_push((AV *)subname, aname);
}
if (len == 1) sv_catpvs(subname, ":");
else {
sv_catpvs(subname, "::");
- sv_catpvn(subname, key, len-2);
- if ( SvUTF8(keysv) )
- SvUTF8_on(subname);
+ sv_catpvn_flags(
+ subname, key, len-2,
+ HeUTF8(entry) ? SV_CATUTF8 : SV_CATBYTES
+ );
}
}
mro_gather_and_rename(
the changes in this one.
Ideally, all instances of C<PL_sub_generation++> in
-perl source outside of C<mro.c> should be
+perl source outside of F<mro.c> should be
replaced by calls to this.
Perl automatically handles most of the common
const bool stashname_utf8 = HvENAMEUTF8(stash) ? 1 : 0;
SV ** const svp = hv_fetch(PL_isarev, stashname,
- stashname_utf8 ? -stashname_len : stashname_len, 0);
+ stashname_utf8 ? -(I32)stashname_len : (I32)stashname_len, 0);
HV * const isarev = svp ? MUTABLE_HV(*svp) : NULL;
PERL_ARGS_ASSERT_MRO_METHOD_CHANGED_IN;
/* Inc the package generation, since a local method changed */
HvMROMETA(stash)->pkg_gen++;
+ /* DESTROY can be cached in SvSTASH. */
+ if (!SvOBJECT(stash)) SvSTASH(stash) = NULL;
+
/* If stash is UNIVERSAL, or one of UNIVERSAL's parents,
invalidate all method caches globally */
if((stashname_len == 9 && strEQ(stashname, "UNIVERSAL"))
mrometa->cache_gen++;
if(mrometa->mro_nextmethod)
hv_clear(mrometa->mro_nextmethod);
+ if (!SvOBJECT(revstash)) SvSTASH(revstash) = NULL;
}
}
+
+ /* The method change may be due to *{$package . "::()"} = \&nil; in
+ overload.pm. */
+ HvAMAGIC_on(stash);
}
void
* Local variables:
* c-indentation-style: bsd
* c-basic-offset: 4
- * indent-tabs-mode: t
+ * indent-tabs-mode: nil
* End:
*
- * ex: set ts=8 sts=4 sw=4 noet:
+ * ex: set ts=8 sts=4 sw=4 et:
*/