File Coverage

next.xs
Criterion Covered Total %
statement 100 126 79.3
branch 82 262 31.3
condition n/a
subroutine n/a
pod n/a
total 182 388 46.9


line stmt bran cond sub pod time code
1             #include
2             #include
3              
4             #define _TRYNEXT(code) { \
5             try { code; } \
6             catch (std::logic_error err) { croak_sv(newSVpvn_flags(err.what(), strlen(err.what()), SVf_UTF8 | SVs_TEMP)); } \
7             }
8              
9             #define PP_METHOD_EXEC(sub) { \
10             dSP; \
11             XPUSHs((SV*)sub); \
12             PUTBACK; \
13             return PL_op->op_next; \
14             }
15              
16             #define PP_SUB_EXEC(sub) { \
17             TOPs = (SV*)sub; \
18             return PL_ppaddr[OP_ENTERSUB](aTHX); \
19             }
20              
21             #define PP_EMPTY_RETURN { \
22             if (GIMME_V == G_SCALAR) *(PL_stack_sp = PL_stack_base + TOPMARK + 1) = &PL_sv_undef; \
23             else PL_stack_sp = PL_stack_base + TOPMARK; \
24             }
25              
26             #define PP_METHOD_MAYBE_EXEC(sub) { \
27             if (sub) { PP_METHOD_EXEC(sub); } \
28             else { \
29             PP_EMPTY_RETURN; \
30             return PL_op->op_next->op_next; \
31             } \
32             }
33              
34             #define PP_SUB_MAYBE_EXEC(sub) { \
35             if (sub) { PP_SUB_EXEC(sub); } \
36             else { \
37             PP_EMPTY_RETURN; \
38             return PL_op->op_next; \
39             } \
40             }
41              
42             #ifdef USE_ITHREADS
43             # define cGVOPx_gv_set(o,gv) (PAD_SVl(cPADOPx(o)->op_padix) = (SV*)gv)
44             #else
45             # define cGVOPx_gv_set(o,gv) (cSVOPx(o)->op_sv = (SV*)gv)
46             #endif
47              
48              
49 63           static void optimize (pTHX_ OP* op, OP* (*pp_method)(pTHX), OP* (*pp_sub)(pTHX), CV* check, GV* payload = NULL) {
50 63 50         if ((op->op_spare & 1) || op->op_type != OP_ENTERSUB || !(op->op_flags & OPf_STACKED) || op->op_ppaddr != PL_ppaddr[OP_ENTERSUB]) return;
    100          
    50          
    50          
51 55           op->op_spare |= 1;
52 55           OP* curop = cUNOPx(op)->op_first;
53 55 50         if (!curop) return; /* Such op can be created by call_sv(G_METHOD_NAMED) */
54 159 100         while (OpHAS_SIBLING(curop)) curop = OpSIBLING(curop);
    50          
55            
56             // optimize METHOD_REDIR $self->next::method
57 55 50         if (curop->op_next == op && curop->op_type == OP_METHOD_REDIR && curop->op_ppaddr == PL_ppaddr[OP_METHOD_REDIR]) {
    100          
    50          
58 48           curop->op_ppaddr = pp_method;
59 48 100         if (!payload) return;
60             // payload will be in cMETHOPx_rclass(PL_op)
61 8           SV* old = cMETHOPx_rclass(curop);
62 8           cMETHOPx_rclass(curop) = (SV*)payload;
63 8           SvREFCNT_inc(payload);
64 8           SvREFCNT_dec(old);
65 8           return;
66             }
67            
68             // OPTIMIZE ENTERSUB FOR CASE next::method($self) - compile-time identified subroutines
69 7 50         if (!OP_TYPE_IS_OR_WAS(curop, OP_LIST)) return;
    50          
    50          
    0          
70 7           curop = cUNOPx(curop)->op_first;
71 7 50         if (!curop) return;
72            
73 22 100         while (OpHAS_SIBLING(curop)) curop = OpSIBLING(curop);
    50          
74 7 50         if (!OP_TYPE_IS_OR_WAS(curop, OP_RV2CV)) return;
    50          
    50          
    0          
75            
76 7           curop = cUNOPx(curop)->op_first;
77 7 50         if (!curop || curop->op_type != OP_GV) return;
    50          
78 7           GV* gv = cGVOPx_gv(curop);
79 7 50         if (GvCV(gv) != check) return;
80            
81 7           op->op_ppaddr = pp_sub;
82            
83 7 50         if (!payload) return;
84             // payload will be in TOPs
85 7           cGVOPx_gv_set(curop, payload);
86 7           SvREFCNT_inc(payload);
87 7           SvREFCNT_dec(gv);
88             }
89              
90 370           static inline HV* proto_stash (pTHX_ SV* proto) {
91 370 100         if (SvROK(proto)) {
92 158           SV* val = SvRV(proto);
93 158 50         if (SvOBJECT(val)) return SvSTASH(val);
94             }
95 212           return gv_stashsv(proto, GV_ADD);
96             }
97              
98 15           static inline GV* get_current_opsub (pTHX_ const char* name, STRLEN len, bool is_utf8, U32 hash) {
99 15           const HE* const ent = (HE*)hv_common(CopSTASH(PL_curcop), NULL, name, len, is_utf8, 0, NULL, hash);
100 15 50         if (ent) return (GV*)HeVAL(ent);
101            
102 0           SV* fqn = sv_newmortal();
103 0 0         sv_catpvn(fqn, HvNAME(CopSTASH(PL_curcop)), HvNAMELEN(CopSTASH(PL_curcop)));
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
104 0           sv_catpvs(fqn, "::");
105 0           sv_catpvn(fqn, name, len);
106 0 0         return gv_fetchpvn_flags(SvPVX(fqn), SvCUR(fqn), GV_ADD|(is_utf8 ? SVf_UTF8 : 0), SVt_PVCV);
107             }
108              
109             // $self->next::can
110 0           static OP* ppm_nextcan (pTHX) {
111 0           PL_stack_sp = PL_stack_base + TOPMARK + 1;
112             CV* sub;
113 0 0         _TRYNEXT({ sub = xs::next::method(aTHX_ proto_stash(aTHX_ *PL_stack_sp)); });
    0          
    0          
114 0 0         *PL_stack_sp = sub ? sv_2mortal(newRV((SV*)sub)) : &PL_sv_undef;
115 0 0         return PL_op->op_next->op_next; // skip ENTERSUB
116             }
117              
118             // next::can($self)
119 0           static OP* pps_nextcan (pTHX) {
120 0           PL_stack_sp = PL_stack_base + TOPMARK + 1;
121             CV* sub;
122 0 0         _TRYNEXT({ sub = xs::next::method(aTHX_ proto_stash(aTHX_ *PL_stack_sp)); });
    0          
    0          
123 0 0         *PL_stack_sp = sub ? sv_2mortal(newRV((SV*)sub)) : &PL_sv_undef;
124 0 0         return PL_op->op_next;
125             }
126              
127             // $self->next::method
128 10           static OP* ppm_next (pTHX) {
129             CV* sub;
130 10 50         _TRYNEXT({ sub = xs::next::method_strict(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1])); });
    50          
    0          
131 10 50         PP_METHOD_EXEC(sub);
    0          
132             }
133              
134             // next::method($self)
135 0           static OP* pps_next (pTHX) {
136 0           dSP;
137             CV* sub;
138 0 0         _TRYNEXT({ sub = xs::next::method_strict(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1])); });
    0          
    0          
139 0 0         PP_SUB_EXEC(sub);
140             }
141              
142             // $self->maybe::next::method
143 0           static OP* ppm_next_maybe (pTHX) {
144             CV* sub;
145 0 0         _TRYNEXT({ sub = xs::next::method(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1])); });
    0          
    0          
146 0 0         PP_METHOD_MAYBE_EXEC(sub);
    0          
    0          
    0          
    0          
147             }
148              
149             // maybe::next::method($self)
150 0           static OP* pps_next_maybe (pTHX) {
151 0           dSP;
152             CV* sub;
153 0 0         _TRYNEXT({ sub = xs::next::method(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1])); });
    0          
    0          
154 0 0         PP_SUB_MAYBE_EXEC(sub);
    0          
    0          
    0          
155             }
156              
157             // $self->super::subname
158 62           static OP* ppm_super (pTHX) {
159             CV* sub;
160 62 50         _TRYNEXT({ sub = xs::super::method_strict(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1]), (GV*)cMETHOPx_rclass(PL_op)); });
    50          
    0          
161 62 50         PP_METHOD_EXEC(sub);
    0          
162             }
163              
164             // super::subname($self)
165 62           static OP* pps_super (pTHX) {
166 62           dSP;
167             CV* sub;
168 62 50         _TRYNEXT({ sub = xs::super::method_strict(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1]), (GV*)TOPs); });
    50          
    0          
169 62 0         PP_SUB_EXEC(sub);
170             }
171              
172             // $self->super::maybe::subname
173 140           static OP* ppm_super_maybe (pTHX) {
174             CV* sub;
175 140 50         _TRYNEXT({ sub = xs::super::method(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1]), (GV*)cMETHOPx_rclass(PL_op)); });
    50          
    0          
176 140 100         PP_METHOD_MAYBE_EXEC(sub);
    50          
    50          
    50          
    0          
177             }
178              
179             // super::maybe::subname($self)
180 33           static OP* pps_super_maybe (pTHX) {
181 33           dSP;
182             CV* sub;
183 33 50         _TRYNEXT({ sub = xs::super::method(aTHX_ proto_stash(aTHX_ PL_stack_base[TOPMARK+1]), (GV*)TOPs); });
    50          
    0          
184 33 50         PP_SUB_MAYBE_EXEC(sub);
    0          
    0          
    0          
185             }
186              
187 15           static void super_xsub (pTHX_ CV* cv) {
188 15           dXSARGS; dXSI32;
189 15 50         if (items < 1) croak_xs_usage(cv, "proto, ...");
190 15           SP -= items;
191 15           SV* proto = ST(0);
192            
193 15           GV* gv = CvGV(cv);
194 15           HEK* hek = GvNAME_HEK(gv);
195 15           GV* context = get_current_opsub(aTHX_ HEK_KEY(hek), HEK_LEN(hek), HEK_UTF8(hek), HEK_HASH(hek));
196            
197             CV* sub;
198 15 100         if (ix == 0) { // super
199 8           optimize(aTHX_ PL_op, &ppm_super, &pps_super, cv, context);
200 8 50         _TRYNEXT({ sub = xs::super::method_strict(aTHX_ proto_stash(aTHX_ proto), context); });
    50          
    0          
    0          
201             } else { // super::maybe
202 7           optimize(aTHX_ PL_op, &ppm_super_maybe, &pps_super_maybe, cv, context);
203 7 50         _TRYNEXT({ sub = xs::super::method(aTHX_ proto_stash(aTHX_ proto), context); });
    50          
    0          
204 7 100         if (!sub) XSRETURN_EMPTY;
205             }
206            
207 14           ENTER;
208 14 50         PUSHMARK(SP);
209 14 50         call_sv((SV*)sub, GIMME_V);
210 15 0         LEAVE;
211             }
212              
213             // This sub is defined by hand instead of XSUB syntax because we MUST NOT do POPMARK, because super_xsub will
214 3           static void super_AUTOLOAD (pTHX_ CV* cv) {
215 3           dXSI32;
216 3 100         SV* fqn = get_sv(ix == 0 ? "super::AUTOLOAD" : "super::maybe::AUTOLOAD", 0);
217 3           CV* xsub = newXS(SvPVX(fqn), super_xsub, __FILE__);
218 3           CvXSUBANY(xsub).any_i32 = ix;
219 3           super_xsub(aTHX_ xsub);
220 3           return;
221             }
222              
223             MODULE = Panda::next PACKAGE = next
224             PROTOTYPES: DISABLE
225              
226             SV* can (SV* proto) {
227 8           optimize(aTHX_ PL_op, &ppm_nextcan, &pps_nextcan, cv);
228             CV* sub;
229 8 50         _TRYNEXT({ sub = xs::next::method(aTHX_ proto_stash(aTHX_ proto)); });
    50          
    0          
230 8 100         RETVAL = sub ? newRV((SV*)sub) : &PL_sv_undef;
231             }
232              
233             void method (SV* proto, ...) {
234 32           optimize(aTHX_ PL_op, &ppm_next, &pps_next, cv);
235            
236             CV* sub;
237 36 50         _TRYNEXT({ sub = xs::next::method_strict(aTHX_ proto_stash(aTHX_ proto)); });
    100          
    50          
238            
239 28           ENTER;
240 28 50         PUSHMARK(SP);
241 28 100         call_sv((SV*)sub, GIMME_V);
242 26           LEAVE;
243 30 50         return;
244             }
245              
246             MODULE = Panda::next PACKAGE = maybe::next
247             PROTOTYPES: DISABLE
248              
249             void method (SV* proto, ...) {
250 8           optimize(aTHX_ PL_op, &ppm_next_maybe, &pps_next_maybe, cv);
251            
252             CV* sub;
253 8 50         _TRYNEXT({ sub = xs::next::method(aTHX_ proto_stash(aTHX_ proto)); });
    50          
    0          
254 8 100         if (!sub) XSRETURN_EMPTY;
255            
256 4           ENTER;
257 4 50         PUSHMARK(SP);
258 4 50         call_sv((SV*)sub, GIMME_V);
259 4           LEAVE;
260 8 0         return;
261             }
262              
263             BOOT {
264 19           cv = newXS_deffile("super::AUTOLOAD", super_AUTOLOAD);
265 19           XSANY.any_i32 = 0;
266 19           cv = newXS_deffile("super::maybe::AUTOLOAD", super_AUTOLOAD);
267 19           XSANY.any_i32 = 1;
268             }