|NULLOK STRLEN *len|NULLOK U32 *flags
xpoM |struct refcounted_he *|store_cop_label \
|NULLOK struct refcounted_he *const chain|NN const char *label
+poM |HV * |get_isa_hash |NN HV *const stash
END_EXTERN_C
/*
if(meta->mro_linear_dfs) SvREFCNT_dec(meta->mro_linear_dfs);
if(meta->mro_linear_c3) SvREFCNT_dec(meta->mro_linear_c3);
if(meta->mro_nextmethod) SvREFCNT_dec(meta->mro_nextmethod);
+ SvREFCNT_dec(meta->isa);
Safefree(meta);
iter->xhv_mro_meta = NULL;
}
U32 cache_gen; /* Bumping this invalidates our method cache */
U32 pkg_gen; /* Bumps when local methods/@ISA change */
const struct mro_alg *mro_which; /* which mro alg is in use? */
+ HV *isa; /* Everything this class @ISA */
};
/* Subject to change.
if (newmeta->mro_nextmethod)
newmeta->mro_nextmethod
= (HV*) SvREFCNT_inc(sv_dup((SV*)newmeta->mro_nextmethod, param));
+ if (newmeta->isa)
+ newmeta->isa
+ = (HV*) SvREFCNT_inc(sv_dup((SV*)newmeta->isa, param));
return newmeta;
}
#endif /* USE_ITHREADS */
+HV *
+Perl_get_isa_hash(pTHX_ HV *const stash)
+{
+ dVAR;
+ struct mro_meta *const meta = HvMROMETA(stash);
+
+ PERL_ARGS_ASSERT_GET_ISA_HASH;
+
+ if (!meta->isa)
+ mro_get_linear_isa_dfs(stash, 0);
+ assert(meta->isa);
+ return meta->isa;
+}
+
/*
=for apidoc mro_get_linear_isa_dfs
AV* av;
const HEK* stashhek;
struct mro_meta* meta;
+ SV *our_name;
+ HV *stored;
PERL_ARGS_ASSERT_MRO_GET_LINEAR_ISA_DFS;
assert(HvAUX(stash));
/* not in cache, make a new one */
retval = (AV*)sv_2mortal((SV *)newAV());
- av_push(retval, newSVhek(stashhek)); /* add ourselves at the top */
+ /* We use this later in this function, but don't need a reference to it
+ beyond the end of this function, so reference count is fine. */
+ our_name = newSVhek(stashhek);
+ av_push(retval, our_name); /* add ourselves at the top */
/* fetch our @ISA */
gvp = (GV**)hv_fetchs(stash, "ISA", FALSE);
av = (gvp && (gv = *gvp) && isGV_with_GP(gv)) ? GvAV(gv) : NULL;
- if(av && AvFILLp(av) >= 0) {
+ /* "stored" is used to keep track of all of the classnames we have added to
+ the MRO so far, so we can do a quick exists check and avoid adding
+ duplicate classnames to the MRO as we go.
+ It's then retained to be re-used as a fast lookup for ->isa(), by adding
+ our own name and "UNIVERSAL" to it. */
- /* "stored" is used to keep track of all of the classnames
- we have added to the MRO so far, so we can do a quick
- exists check and avoid adding duplicate classnames to
- the MRO as we go. */
+ stored = (HV*)sv_2mortal((SV*)newHV());
+
+ if(av && AvFILLp(av) >= 0) {
- HV* const stored = (HV*)sv_2mortal((SV*)newHV());
SV **svp = AvARRAY(av);
I32 items = AvFILLp(av) + 1;
mortals' stack will be released soon, so everything will balance. */
SvREFCNT_inc_simple_void_NN(retval);
SvTEMP_off(retval);
+ SvREFCNT_inc_simple_void_NN(stored);
+ SvTEMP_off(stored);
+
+ hv_store_ent(stored, our_name, &PL_sv_undef, 0);
+ hv_store(stored, "UNIVERSAL", 9, &PL_sv_undef, 0);
/* we don't want anyone modifying the cache entry but us,
and we do so by replacing it completely */
SvREADONLY_on(retval);
+ SvREADONLY_on(stored);
meta->mro_linear_dfs = retval;
+ meta->isa = stored;
return retval;
}
#define PERL_ARGS_ASSERT_STORE_COP_LABEL \
assert(label)
+PERL_CALLCONV HV * Perl_get_isa_hash(pTHX_ HV *const stash)
+ __attribute__nonnull__(pTHX_1);
+#define PERL_ARGS_ASSERT_GET_ISA_HASH \
+ assert(stash)
+
END_EXTERN_C
/*
S_isa_lookup(pTHX_ HV *stash, const char * const name)
{
dVAR;
- AV* stash_linear_isa;
- SV** svp;
- const char *hvname;
- I32 items;
- const HV *const name_stash = gv_stashpv(name, 0);
+ const struct mro_meta *const meta = HvMROMETA(stash);
+ HV *const isa = meta->isa ? meta->isa : Perl_get_isa_hash(aTHX_ stash);
+ STRLEN len = strlen(name);
+ const HV *our_stash;
PERL_ARGS_ASSERT_ISA_LOOKUP;
- /* A stash/class can go by many names (ie. User == main::User), so
- we compare the stash itself just in case */
- if ((const HV *)stash == name_stash)
- return TRUE;
-
- hvname = HvNAME_get(stash);
-
- if (strEQ(hvname, name))
+ if (hv_common(isa, NULL, name, len, 0 /* No "UTF-8" flag possible with only
+ a char * argument*/,
+ HV_FETCH_ISEXISTS, NULL, 0)) {
+ /* Direct name lookup worked. */
return TRUE;
+ }
- if (strEQ(name, "UNIVERSAL"))
- return TRUE;
+ /* A stash/class can go by many names (ie. User == main::User), so
+ we use the name in the stash itself, which is canonical. */
+ our_stash = gv_stashpvn(name, len, 0);
- stash_linear_isa = mro_get_linear_isa(stash);
- svp = AvARRAY(stash_linear_isa) + 1;
- items = AvFILLp(stash_linear_isa);
- while (items--) {
- SV* const basename_sv = *svp++;
- HV* const basestash = gv_stashsv(basename_sv, 0);
- if(name_stash == basestash || strEQ(name, SvPVX(basename_sv)))
+ if (our_stash) {
+ HEK *const canon_name = HvNAME_HEK(our_stash);
+
+ if (hv_common(isa, NULL, HEK_KEY(canon_name), HEK_LEN(canon_name),
+ HEK_FLAGS(canon_name),
+ HV_FETCH_ISEXISTS, NULL, HEK_HASH(canon_name))) {
return TRUE;
+ }
}
return FALSE;