File Coverage

lib/overload/substr.xs
Criterion Covered Total %
statement 102 121 84.3
branch 41 90 45.5
condition n/a
subroutine n/a
pod n/a
total 143 211 67.7


line stmt bran cond sub pod time code
1             /* You may distribute under the terms of either the GNU General Public License
2             * or the Artistic License (the same terms as Perl itself)
3             *
4             * (C) Paul Evans, 2010 -- leonerd@leonerd.org.uk
5             *
6             * Much of this code inspired by http://search.cpan.org/~jjore/UNIVERSAL-ref-0.12/
7             */
8              
9             #include "EXTERN.h"
10             #include "perl.h"
11             #include "XSUB.h"
12              
13             #define HAVE_PERL_VERSION(R, V, S) \
14             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
15              
16             #if HAVE_PERL_VERSION(5,41,8)
17             # define HAVE_OP_SUBSTR_LEFT
18             #endif
19              
20             static int init_done = 0;
21              
22             typedef struct {
23             GV *substr_method;
24             SV *offset;
25             SV *length;
26             } overload_substr_ctx;
27              
28 1           static int magic_get(pTHX_ SV *sv, MAGIC *mg)
29             {
30 1           dSP;
31 1           overload_substr_ctx *ctx = (void *)mg->mg_ptr;
32             SV *result;
33             int count;
34              
35 1           ENTER;
36 1           SAVETMPS;
37              
38 1 50         PUSHMARK(SP);
39 1 50         XPUSHs(mg->mg_obj);
40 1 50         XPUSHs(ctx->offset);
41 1 50         if(ctx->length)
42 1 50         XPUSHs(ctx->length);
43             else
44 0 0         XPUSHs(&PL_sv_undef);
45 1           PUTBACK;
46              
47 1           count = call_sv((SV*)GvCV(ctx->substr_method), G_SCALAR);
48             assert(count == 1);
49              
50 1           SPAGAIN;
51 1           result = POPs;
52              
53 1           sv_setsv_nomg(sv, result);
54              
55 1           PUTBACK;
56 1 50         FREETMPS;
57 1           LEAVE;
58              
59 1           return 1;
60             }
61              
62 0           static int magic_set(pTHX_ SV *sv, MAGIC *mg)
63             {
64 0           dSP;
65 0           overload_substr_ctx *ctx = (void *)mg->mg_ptr;
66              
67 0           ENTER;
68 0           SAVETMPS;
69              
70 0 0         PUSHMARK(SP);
71 0 0         XPUSHs(mg->mg_obj);
72 0 0         XPUSHs(ctx->offset);
73 0 0         if(ctx->length)
74 0 0         XPUSHs(ctx->length);
75             else
76 0 0         XPUSHs(&PL_sv_undef);
77 0 0         XPUSHs(sv);
78 0           PUTBACK;
79              
80 0           call_sv((SV*)GvCV(ctx->substr_method), G_SCALAR|G_DISCARD);
81              
82 0 0         FREETMPS;
83 0           LEAVE;
84              
85 0           return 1;
86             }
87              
88 1           static int magic_free(pTHX_ SV *sv, MAGIC *mg)
89             {
90 1           overload_substr_ctx *ctx = (void *)mg->mg_ptr;
91              
92 1           SvREFCNT_dec(ctx->substr_method);
93 1           SvREFCNT_dec(ctx->offset);
94 1 50         if(ctx->length)
95 1           SvREFCNT_dec(ctx->length);
96              
97 1           Safefree(ctx);
98              
99 1           return 1;
100             }
101              
102 5995           static GV *get_substr_method(SV *sv)
103             {
104 5995 100         if(!sv_isobject(sv))
105             return NULL;
106              
107 11           return gv_fetchmeth(SvSTASH(SvRV(sv)), "(substr", 7, 0);
108             }
109              
110             static MGVTBL vtbl = {
111             &magic_get,
112             &magic_set,
113             NULL, /* len */
114             NULL, /* clear */
115             &magic_free,
116             };
117              
118             static OP *(*real_pp_substr)(pTHX);
119 5990           PP(pp_overload_substr) {
120 5990           dSP; dTARG;
121 5990           const int num_args = PL_op->op_private & 7; /* Horrible; stolen from pp.c:pp_subst */
122 5990           SV *self = *(SP - num_args + 1);
123             GV *substr_method;
124             SV *result;
125              
126 5990           substr_method = get_substr_method(self);
127 5990 100         if(!substr_method)
128 5983           return (*real_pp_substr)(aTHX);
129              
130             #ifdef OPpSUBSTR_REPL_FIRST
131 7 100         if(PL_op->op_private & OPpSUBSTR_REPL_FIRST) {
132             /* This flag means that the replacement comes first, before num_args
133             * Easiest is to push it as the 4th argument then call the method
134             */
135 3           SV *replacement = SP[-num_args];
136              
137 3           ENTER;
138 3           SAVETMPS;
139              
140 3 50         PUSHMARK(SP-num_args);
141 3 100         if(num_args < 3)
142 1 50         XPUSHs(&PL_sv_undef);
143 3 50         XPUSHs(replacement);
144 3           PUTBACK;
145              
146 3           call_sv((SV*)GvCV(substr_method), G_SCALAR|G_DISCARD);
147              
148 3 50         FREETMPS;
149 3           LEAVE;
150              
151 3           RETURN;
152             }
153             #endif
154              
155 4 100         if(PL_op->op_flags & OPf_MOD || LVRET) {
    50          
    0          
156             overload_substr_ctx *ctx;
157             MAGIC *mg;
158              
159 1           Newx(ctx, 1, overload_substr_ctx);
160              
161 1           ctx->substr_method = (GV*)SvREFCNT_inc(substr_method);
162              
163 1 50         if(num_args == 3)
164 2 50         ctx->length = SvREFCNT_inc(POPs);
165             else
166 0           ctx->length = NULL;
167              
168 1 50         ctx->offset = SvREFCNT_inc(POPs);
169 1           POPs; /* self */
170              
171 1           result = sv_2mortal(newSVpvn("", 0));
172              
173 1           mg = sv_magicext(result, self, PERL_MAGIC_ext, &vtbl, (void *)ctx, 0);
174              
175 1 50         XPUSHs(result);
176 1           RETURN;
177             }
178              
179 3           ENTER;
180 3           SAVETMPS;
181              
182             /* This piece of evil trickery "pushes" all the args we already have on the
183             * stack, by simply claiming the MARK to be at the bottom of this op's args
184             */
185 3 50         PUSHMARK(SP-num_args);
186 3           PUTBACK;
187              
188 3           call_sv((SV*)GvCV(substr_method), G_SCALAR);
189              
190 3           SPAGAIN;
191 3 50         result = POPs;
192              
193             SvREFCNT_inc(result);
194              
195 3 50         FREETMPS;
196 3           LEAVE;
197              
198 3 50         XPUSHs(result);
199              
200 3           RETURN;
201             }
202              
203             #ifdef HAVE_OP_SUBSTR_LEFT
204             static OP *(*real_pp_substr_left)(pTHX);
205 5           PP(pp_overload_substr_left) {
206 5           dSP; dTARGET;
207 5           SV *self = SP[-1];
208             GV *substr_method;
209             SV *result;
210              
211 5           substr_method = get_substr_method(self);
212 5 100         if(!substr_method)
213 1           return (*real_pp_substr_left)(aTHX);
214              
215             /* OP_SUBSTR_LEFT does not have the OPpSUBSTR_REPL_FIRST bit */
216             assert(!(PL_op->op_flags & OPf_MOD));
217             assert(!LVRET);
218              
219 4 50         bool rvalue = (GIMME_V != G_VOID) || (PL_op->op_private & OPpTARGET_MY);
    50          
220 4           SV *len = SP[0];
221              
222 4           ENTER;
223 4           SAVETMPS;
224              
225 4 50         EXTEND(SP, 3);
226 4 50         PUSHMARK(SP);
227 4           PUSHs(self);
228 4           mPUSHi(0); /* offset is always zero */
229 4           PUSHs(len);
230             /* no replacement */
231 4           PUTBACK;
232              
233 4           call_sv((SV*)GvCV(substr_method), G_SCALAR);
234              
235 4           SPAGAIN;
236 4 50         result = POPs;
237              
238             SvREFCNT_inc(result);
239              
240 4 50         FREETMPS;
241 4           LEAVE;
242              
243 4           sv_setsv(TARG, result);
244              
245 4 50         if(rvalue)
246 4 50         XPUSHs(result);
247              
248 4           RETURN;
249             }
250             #endif
251              
252             MODULE = overload::substr PACKAGE = overload::substr
253              
254             BOOT:
255 6 50         if(!init_done++) {
256 6           real_pp_substr = PL_ppaddr[OP_SUBSTR];
257 6           PL_ppaddr[OP_SUBSTR] = &Perl_pp_overload_substr;
258             #ifdef HAVE_OP_SUBSTR_LEFT
259 6           real_pp_substr_left = PL_ppaddr[OP_SUBSTR_LEFT];
260 6           PL_ppaddr[OP_SUBSTR_LEFT] = &Perl_pp_overload_substr_left;
261             #endif
262             }