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