File Coverage

src/xs/next.cc
Criterion Covered Total %
statement 112 136 82.3
branch 131 284 46.1
condition n/a
subroutine n/a
pod n/a
total 243 420 57.8


line stmt bran cond sub pod time code
1             #include "next.h"
2             #include
3             #include
4              
5             namespace xs {
6              
7             static MGVTBL c3_marker;
8              
9             #ifndef FORCEINLINE
10             # if defined(_MSC_VER)
11             # define FORCEINLINE __forceinline
12             # elif defined(__GNUC_) && _GNUC__ > 3
13             # define FORCEINLINE inline _attribute_ ((_always_inline_))
14             # else
15             # define FORCEINLINE inline
16             # endif
17             #endif
18              
19             #if PERL_VERSION >= 20
20             #define HvSUPERCACHE(hv) (HvMROMETA(stash)->super)
21             #else
22             #define HvSUPERCACHE(hv) (HvAUX(stash)->xhv_super)
23             #endif
24              
25 63           static FORCEINLINE I32 __dopoptosub_at (const PERL_CONTEXT* cxstk, I32 startingblock) {
26             I32 i;
27 69 100         for (i = startingblock; i >= 0; --i) if (CxTYPE(cxstk+i) == CXt_SUB) return i;
    100          
28 1           return i;
29             }
30              
31             // 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
32 56           static FORCEINLINE GV* _find_sub (SV** fqnp) {
33             dTHX;
34 56           const PERL_SI* top_si = PL_curstackinfo;
35 56           const PERL_CONTEXT* ccstack = cxstack;
36 56           I32 cxix = __dopoptosub_at(ccstack, cxstack_ix);
37              
38 62           for (;;) {
39             /* we may be in a higher stacklevel, so dig down deeper */
40 63 100         while (cxix < 0) {
41 1 50         if (top_si->si_type == PERLSI_MAIN) throw std::logic_error("next::method/next::can/maybe::next::method must be used in method context");
    50          
42 0           top_si = top_si->si_prev;
43 0           ccstack = top_si->si_cxstack;
44 0           cxix = __dopoptosub_at(ccstack, top_si->si_cxix);
45             }
46              
47 62 50         if (PL_DBsub && GvCV(PL_DBsub)) {
    50          
48 0 0         if (ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub)) {
49 0           cxix = __dopoptosub_at(ccstack, cxix - 1);
50 0           continue;
51             }
52 0           const I32 dbcxix = __dopoptosub_at(ccstack, cxix - 1);
53 0 0         if (dbcxix >= 0 && ccstack[dbcxix].blk_sub.cv == GvCV(PL_DBsub)) {
    0          
54 0 0         if (CxTYPE((PERL_CONTEXT*)(&ccstack[dbcxix])) != CXt_SUB) {
55 0           cxix = dbcxix;
56 0           continue;
57             }
58             }
59             }
60              
61             /* we found a real sub here */
62 62           CV* cv = ccstack[cxix].blk_sub.cv;
63 62           GV* gv = CvGV(cv);
64 62           HV* stash = GvSTASH(gv);
65              
66 62           MAGIC* mg = mg_findext((SV*)gv, PERL_MAGIC_ext, &c3_marker);
67 62 100         if (mg && (HV*)mg->mg_ptr == stash) {
    50          
68 10           *fqnp = mg->mg_obj;
69 10           return gv;
70             }
71              
72 52 50         if (!stash || !HvNAME(stash) || (GvNAMELEN(gv) == 8 && !memcmp(GvNAME(gv), "__ANON__", 8))) { // ANON sub
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    100          
    100          
73 7           cxix = __dopoptosub_at(ccstack, cxix - 1);
74 7           continue;
75             }
76              
77 45           return gv;
78             }
79             }
80              
81 55           static FORCEINLINE SV* _make_shared_fqn (pTHX_ GV* gv) {
82 55           HV* stash = GvSTASH(gv);
83 55 50         STRLEN pkglen = HvNAMELEN(stash);
    50          
    50          
    0          
    50          
    50          
84 55           STRLEN fqnlen = pkglen + GvNAMELEN(gv) + 2;
85 55           char fqn[fqnlen+1];
86 55 50         memcpy(fqn, HvNAME(stash), pkglen);
    50          
    50          
    0          
    50          
    50          
87 55           fqn[pkglen] = ':';
88 55           fqn[pkglen+1] = ':';
89 55           memcpy(fqn + pkglen + 2, GvNAME(gv), GvNAMELEN(gv));
90 55           fqn[fqnlen] = 0;
91 55 50         return newSVpvn_share(fqn, (HvNAMEUTF8(stash) || GvNAMEUTF8(gv)) ? -(I32)fqnlen : (I32)fqnlen, 0);
    50          
    50          
    0          
    50          
    50          
    50          
    100          
    50          
    50          
92             }
93              
94 2           static FORCEINLINE void _throw_no_next_method (HV* selfstash, GV* context) {
95 4 50         std::string subname(GvNAME(context), GvNAMELEN(context));
96 4 50         std::string stashname(HvNAME(selfstash), HvNAMELEN(selfstash));
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
97 2 50         throw std::logic_error(std::string("No next::method '") + subname + "' found for " + stashname);
    50          
    50          
    50          
    50          
98             }
99              
100 139           static FORCEINLINE CV* _method (HV* selfstash, GV* context, SV* fqnsv) {
101 139 100         if (!fqnsv) { // cache FQN SV with shared COW hash of current sub in magic to perform hash lookup with precomputed hash
102 129           HV* stash = GvSTASH(context);
103 129           MAGIC* mg = mg_findext((SV*)context, PERL_MAGIC_ext, &c3_marker);
104 129 100         if (!mg || (HV*)mg->mg_ptr != stash) {
    50          
105             dTHX;
106 55 50         if (mg) sv_unmagicext((SV*)context, PERL_MAGIC_ext, &c3_marker);
107 55           bool had_magic = SvRMAGICAL(context);
108 55           fqnsv = _make_shared_fqn(aTHX_ context);
109 55           mg = sv_magicext((SV*)context, fqnsv, PERL_MAGIC_ext, &c3_marker, (const char*)stash, 0);
110 55           mg->mg_flags |= MGf_REFCOUNTED;
111 55 50         if (!had_magic) SvRMAGICAL_off(context);
112             }
113 129           fqnsv = mg->mg_obj;
114             }
115              
116             dTHX;
117 139 100         struct mro_meta* selfmeta = HvMROMETA(selfstash);
118 139           HV* nmcache = selfmeta->mro_nextmethod;
119 139 100         if (nmcache) { // Use the cached coderef if it exists
120 119           HE* he = hv_fetch_ent(nmcache, fqnsv, 0, 0);
121 119 100         if (he) {
122 80           SV* const val = HeVAL(he);
123 119 100         return val == &PL_sv_undef ? NULL : (CV*)val;
124             }
125             }
126 20           else nmcache = selfmeta->mro_nextmethod = newHV(); //Initialize the next::method cache for this stash if necessary
127              
128             /* beyond here is just for cache misses, so perf isn't as critical */
129 59           HV* stash = GvSTASH(context);
130 59           char* subname = GvNAME(context);
131 59           STRLEN subname_len = GvNAMELEN(context);
132 59           bool subname_utf8 = GvNAMEUTF8(context);
133 59 50         char* stashname = HvNAME(stash);
    50          
    50          
    0          
    50          
    50          
134 59 50         STRLEN stashname_len = HvNAMELEN(stash);
    50          
    50          
    0          
    50          
    50          
135              
136             /* has ourselves at the top of the list */
137 59           const mro_alg*const algo = Perl_mro_get_from_name(aTHX_ sv_2mortal(newSVpvs("c3")));
138 59           AV* linear_av = algo->resolve(aTHX_ selfstash, 0);
139 59           SV** linear_svp = AvARRAY(linear_av);
140 59           I32 entries = AvFILLp(linear_av) + 1;
141              
142             /* Walk down our MRO, skipping everything up to the contextually enclosing class */
143 90 100         while (entries--) {
144 89           SV*const linear_sv = *linear_svp++;
145             assert(linear_sv);
146 89 100         if (SvCUR(linear_sv) == stashname_len && !memcmp(SvPVX(linear_sv), stashname, stashname_len)) break;
    100          
147             }
148              
149             /* Now search the remainder of the MRO for the same method name as the contextually enclosing method */
150 59 100         if (entries > 0) {
151 94 100         while (entries--) {
152 86           SV*const linear_sv = *linear_svp++;
153             assert(linear_sv);
154 86           HV* curstash = gv_stashsv(linear_sv, 0);
155              
156 86 50         if (!curstash) {
157 0 0         if (ckWARN(WARN_SYNTAX)) Perl_warner(aTHX_
    0          
    0          
158             packWARN(WARN_SYNTAX), "Can't locate package %" SVf " for @%" HEKf "::ISA",
159 0 0         (void*)linear_sv, HEKfARG( HvNAME_HEK(selfstash) )
160 0           );
161 0           continue;
162             }
163              
164 86 100         GV** gvp = (GV**)hv_fetch(curstash, subname, subname_utf8 ? -(I32)subname_len : (I32)subname_len, 0);
165 86 100         if (!gvp) continue;
166              
167 49           GV* candidate = *gvp;
168             assert(candidate);
169              
170 49 50         if (SvTYPE(candidate) != SVt_PVGV)
171 0 0         gv_init_pvn(candidate, curstash, subname, subname_len, GV_ADDMULTI|(subname_utf8 ? SVf_UTF8 : 0));
172              
173             /* Notably, we only look for real entries, not method cache
174             entries, because in C3 the method cache of a parent is not
175             valid for the child */
176             CV* cand_cv;
177 49 50         if (SvTYPE(candidate) == SVt_PVGV && (cand_cv = GvCV(candidate)) && !GvCVGEN(candidate)) {
    50          
    50          
    50          
178 49           SvREFCNT_inc_simple_void_NN(MUTABLE_SV(cand_cv));
179 49           hv_store_ent(nmcache, fqnsv, MUTABLE_SV(cand_cv), 0);
180 49           return cand_cv;
181             }
182             }
183             }
184              
185 10           hv_store_ent(nmcache, fqnsv, &PL_sv_undef, 0);
186 10           return NULL;
187             }
188              
189 15           CV* next::method (HV* selfstash) {
190 15           SV* fqn = NULL;
191 15 50         GV* context = _find_sub(&fqn);
192 15 50         return _method(selfstash, context, fqn);
193             }
194              
195 41           CV* next::method_strict (HV* selfstash) {
196 41           SV* fqn = NULL;
197 41 100         GV* context = _find_sub(&fqn);
198 40 50         CV* ret = _method(selfstash, context, fqn);
199 40 100         if (!ret) _throw_no_next_method(selfstash, context);
    50          
200 38           return ret;
201             }
202              
203 0           CV* next::method (HV* selfstash, GV* context) { return _method(selfstash, context, NULL); }
204              
205 0           CV* next::method_strict (HV* selfstash, GV* context) {
206 0           CV* ret = _method(selfstash, context, NULL);
207 0 0         if (!ret) _throw_no_next_method(selfstash, context);
208 0           return ret;
209             }
210              
211 312           static FORCEINLINE CV* _super_method (HV* selfstash, GV* context) {
212             //omit comparing strings for speed
213             dTHX;
214 312 50         if (HvMROMETA(selfstash)->mro_which->length != 3) return _method(selfstash, context, NULL); // C3
    100          
215             // DFS
216 228           HV* stash = GvSTASH(context);
217 228           HEK* hek = GvNAME_HEK(context);
218 228 50         HV* cache = HvSUPERCACHE(stash);
219 228 100         if (cache) {
220 224           const HE* const he = (HE*)hv_common(cache, NULL, HEK_KEY(hek), HEK_LEN(hek), HEK_UTF8(hek), 0, NULL, HEK_HASH(hek));
221 224 100         if (he) {
222 218           GV* gv = MUTABLE_GV(HeVAL(he));
223 224 50         if (isGV(gv) && (!GvCVGEN(gv) || GvCVGEN(gv) == (PL_sub_generation + HvMROMETA(stash)->cache_gen))) return GvCV(gv);
    50          
    50          
    50          
    50          
224             }
225             }
226 10           GV* ret = gv_fetchmethod_pvn_flags(stash, HEK_KEY(hek), HEK_LEN(hek), GV_AUTOLOAD|GV_SUPER);
227 10 100         return ret ? (isGV(ret) ? GvCV(ret) : (CV*)ret) : NULL;
    50          
228             }
229              
230 360           CV* super::method (HV* selfstash, GV* context) { return _super_method(selfstash, context); }
231              
232 132           CV* super::method_strict (HV* selfstash, GV* context) {
233 132           CV* ret = _super_method(selfstash, context);
234 132 50         if (!ret) {
235 0 0         std::string subname(GvNAME(context), GvNAMELEN(context));
236 0 0         std::string stashname(HvNAME(selfstash), HvNAMELEN(selfstash));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
237 0 0         throw std::logic_error(std::string("No super::") + subname + " found for " + stashname);
    0          
    0          
    0          
    0          
238             }
239 132           return ret;
240             }
241              
242             }