-XS(XS_mro_get_pkg_gen)
-{
- dVAR;
- dXSARGS;
- SV* classname;
- HV* class_stash;
-
- PERL_UNUSED_ARG(cv);
-
- if(items != 1)
- Perl_croak(aTHX_ "Usage: mro::get_pkg_gen(classname)");
-
- classname = ST(0);
-
- class_stash = gv_stashsv(classname, 0);
-
- SP -= items;
-
- XPUSHs(sv_2mortal(newSViv(
- class_stash ? HvMROMETA(class_stash)->pkg_gen : 0
- )));
-
- PUTBACK;
- return;
-}
-
-XS(XS_mro_nextcan)
-{
- dVAR;
- dXSARGS;
- SV* self = ST(0);
- const I32 throw_nomethod = SvIVX(ST(1));
- register I32 cxix = cxstack_ix;
- register const PERL_CONTEXT *ccstack = cxstack;
- const PERL_SI *top_si = PL_curstackinfo;
- HV* selfstash;
- SV *stashname;
- const char *fq_subname;
- const char *subname;
- STRLEN stashname_len;
- STRLEN subname_len;
- SV* sv;
- GV** gvp;
- AV* linear_av;
- SV** linear_svp;
- const char *hvname;
- I32 entries;
- struct mro_meta* selfmeta;
- HV* nmcache;
- I32 i;
-
- PERL_UNUSED_ARG(cv);
-
- SP -= items;
-
- if(sv_isobject(self))
- selfstash = SvSTASH(SvRV(self));
- else
- selfstash = gv_stashsv(self, 0);
-
- assert(selfstash);
-
- hvname = HvNAME_get(selfstash);
- if (!hvname)
- Perl_croak(aTHX_ "Can't use anonymous symbol table for method lookup");
-
- /* This block finds the contextually-enclosing fully-qualified subname,
- much like looking at (caller($i))[3] until you find a real sub that
- isn't ANON, etc (also skips over pureperl next::method, etc) */
- for(i = 0; i < 2; i++) {
- cxix = __dopoptosub_at(ccstack, cxix);
- for (;;) {
- GV* cvgv;
- STRLEN fq_subname_len;
-
- /* we may be in a higher stacklevel, so dig down deeper */
- while (cxix < 0) {
- if(top_si->si_type == PERLSI_MAIN)
- Perl_croak(aTHX_ "next::method/next::can/maybe::next::method must be used in method context");
- top_si = top_si->si_prev;
- ccstack = top_si->si_cxstack;
- cxix = __dopoptosub_at(ccstack, top_si->si_cxix);
- }
-
- if(CxTYPE((PERL_CONTEXT*)(&ccstack[cxix])) != CXt_SUB
- || (PL_DBsub && GvCV(PL_DBsub) && ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))) {
- cxix = __dopoptosub_at(ccstack, cxix - 1);
- continue;
- }
-
- {
- const I32 dbcxix = __dopoptosub_at(ccstack, cxix - 1);
- if (PL_DBsub && GvCV(PL_DBsub) && dbcxix >= 0 && ccstack[dbcxix].blk_sub.cv == GvCV(PL_DBsub)) {
- if(CxTYPE((PERL_CONTEXT*)(&ccstack[dbcxix])) != CXt_SUB) {
- cxix = dbcxix;
- continue;
- }
- }
- }
-
- cvgv = CvGV(ccstack[cxix].blk_sub.cv);
-
- if(!isGV(cvgv)) {
- cxix = __dopoptosub_at(ccstack, cxix - 1);
- continue;
- }
-
- /* we found a real sub here */
- sv = sv_2mortal(newSV(0));
-
- gv_efullname3(sv, cvgv, NULL);
-
- fq_subname = SvPVX(sv);
- fq_subname_len = SvCUR(sv);
-
- subname = strrchr(fq_subname, ':');
- if(!subname)
- Perl_croak(aTHX_ "next::method/next::can/maybe::next::method cannot find enclosing method");
-
- subname++;
- subname_len = fq_subname_len - (subname - fq_subname);
- if(subname_len == 8 && strEQ(subname, "__ANON__")) {
- cxix = __dopoptosub_at(ccstack, cxix - 1);
- continue;
- }
- break;
- }
- cxix--;
- }
-
- /* If we made it to here, we found our context */
-
- /* Initialize the next::method cache for this stash
- if necessary */
- selfmeta = HvMROMETA(selfstash);
- if(!(nmcache = selfmeta->mro_nextmethod)) {
- nmcache = selfmeta->mro_nextmethod = newHV();
- }
- else { /* Use the cached coderef if it exists */
- HE* cache_entry = hv_fetch_ent(nmcache, sv, 0, 0);
- if (cache_entry) {
- SV* const val = HeVAL(cache_entry);
- if(val == &PL_sv_undef) {
- if(throw_nomethod)
- Perl_croak(aTHX_ "No next::method '%s' found for %s", subname, hvname);
- XSRETURN_EMPTY;
- }
- XPUSHs(sv_2mortal(newRV_inc(val)));
- XSRETURN(1);
- }
- }
-
- /* beyond here is just for cache misses, so perf isn't as critical */
-
- stashname_len = subname - fq_subname - 2;
- stashname = sv_2mortal(newSVpvn(fq_subname, stashname_len));
-
- linear_av = mro_get_linear_isa_c3(selfstash, 0); /* has ourselves at the top of the list */
-
- linear_svp = AvARRAY(linear_av);
- entries = AvFILLp(linear_av) + 1;
-
- /* Walk down our MRO, skipping everything up
- to the contextually enclosing class */
- while (entries--) {
- SV * const linear_sv = *linear_svp++;
- assert(linear_sv);
- if(sv_eq(linear_sv, stashname))
- break;
- }
-
- /* Now search the remainder of the MRO for the
- same method name as the contextually enclosing
- method */
- if(entries > 0) {
- while (entries--) {
- SV * const linear_sv = *linear_svp++;
- HV* curstash;
- GV* candidate;
- CV* cand_cv;
-
- assert(linear_sv);
- curstash = gv_stashsv(linear_sv, FALSE);
-
- if (!curstash) {
- if (ckWARN(WARN_SYNTAX))
- Perl_warner(aTHX_ packWARN(WARN_SYNTAX), "Can't locate package %"SVf" for @%s::ISA",
- (void*)linear_sv, hvname);
- continue;
- }
-
- assert(curstash);
-
- gvp = (GV**)hv_fetch(curstash, subname, subname_len, 0);
- if (!gvp) continue;
-
- candidate = *gvp;
- assert(candidate);
-
- if (SvTYPE(candidate) != SVt_PVGV)
- gv_init(candidate, curstash, subname, subname_len, TRUE);
-
- /* Notably, we only look for real entries, not method cache
- entries, because in C3 the method cache of a parent is not
- valid for the child */
- if (SvTYPE(candidate) == SVt_PVGV && (cand_cv = GvCV(candidate)) && !GvCVGEN(candidate)) {
- SvREFCNT_inc_simple_void_NN((SV*)cand_cv);
- hv_store_ent(nmcache, newSVsv(sv), (SV*)cand_cv, 0);
- XPUSHs(sv_2mortal(newRV_inc((SV*)cand_cv)));
- XSRETURN(1);
- }
- }
- }
-
- hv_store_ent(nmcache, newSVsv(sv), &PL_sv_undef, 0);
- if(throw_nomethod)
- Perl_croak(aTHX_ "No next::method '%s' found for %s", subname, hvname);
- XSRETURN_EMPTY;
-}
-