File Coverage

autobox.xs
Criterion Covered Total %
statement 134 154 87.0
branch 100 146 68.4
condition n/a
subroutine n/a
pod n/a
total 234 300 78.0


line stmt bran cond sub pod time code
1             #define PERL_CORE
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6             #define NEED_sv_2pv_flags
7             #include "ppport.h"
8              
9             #include "ptable.h"
10              
11             static PTABLE_t *AUTOBOX_OP_MAP = NULL;
12             static U32 AUTOBOX_SCOPE_DEPTH = 0;
13             static OP *(*autobox_old_check_entersub)(pTHX_ OP *op) = NULL;
14              
15             static SV * autobox_method_common(pTHX_ SV *method, U32 *hashp);
16             static const char * autobox_type(pTHX_ SV * const sv, STRLEN *len);
17             static void autobox_cleanup(pTHX_ void * unused);
18              
19             OP * autobox_check_entersub(pTHX_ OP *o);
20             OP * autobox_method_named(pTHX);
21             OP * autobox_method(pTHX);
22              
23             void auto_ref(pTHX_ OP *invocant, UNOP *parent, OP *prev);
24              
25             /* handle non-reference invocants e.g. @foo->bar, %foo->bar etc. */
26 25           void auto_ref(pTHX_ OP *invocant, UNOP *parent, OP *prev) {
27             /*
28             * perlref:
29             *
30             * As a special case, "\(@foo)" returns a list of references to the
31             * contents of @foo, not a reference to @foo itself. Likewise for %foo,
32             * except that the key references are to copies (since the keys are just
33             * strings rather than full-fledged scalars).
34             *
35             * we don't want that (it results in the invocant being a reference to the
36             * last element in the list), so we toggle the parentheses off while creating
37             * the reference then toggle them back on in case they're needed elsewhere
38             *
39             */
40 25           bool toggled = FALSE;
41              
42 25 100         if (invocant->op_flags & OPf_PARENS) {
43 8           invocant->op_flags &= ~OPf_PARENS;
44 8           toggled = TRUE;
45             }
46              
47             #ifdef op_sibling_splice
48 25           op_sibling_splice(
49             (OP *)parent,
50             prev,
51             0,
52             newUNOP(
53             OP_REFGEN,
54             0,
55             op_sibling_splice(
56             (OP *)parent,
57             prev,
58             1,
59             NULL
60             )
61             )
62             );
63             #else
64             /* XXX if this (old?) way works, why do we need both? */
65             OP *refgen = newUNOP(OP_REFGEN, 0, invocant);
66             prev->op_sibling = refgen;
67             refgen->op_sibling = invocant->op_sibling;
68             invocant->op_sibling = NULL;
69             #endif
70              
71             /* Restore the parentheses in case something else expects them */
72 25 100         if (toggled) {
73 8           invocant->op_flags |= OPf_PARENS;
74             }
75 25           }
76              
77 2554           OP * autobox_check_entersub(pTHX_ OP *o) {
78             UNOP *parent;
79             OP *prev, *invocant, *cvop;
80             SV **svp;
81             HV *hh;
82 2554           bool has_bindings = FALSE;
83              
84             /*
85             * XXX note: perl adopts a convention of calling the OP `o` and has shortcut
86             * macros based on this convention like cUNOPo, among others. if the name
87             * changes, the macro will need to change as well e.g. to cUNOPx(op)
88             */
89              
90             /*
91             * work around a %^H scoping bug by checking that PL_hints (which is
92             * properly scoped) & an unused PL_hints bit (0x100000) is true
93             *
94             * XXX this is fixed in #33311:
95             *
96             * http://www.nntp.perl.org/group/perl.perl5.porters/2008/02/msg134131.html
97             */
98 2554 100         if ((PL_hints & 0x80020000) != 0x80020000) {
99 1245           goto done;
100             }
101              
102             /*
103             * the OP which yields the CV is the last OP in the ENTERSUB OP's list of
104             * children. navigate to it by following the `op_sibling` pointers from the
105             * first child in the list (the invocant)
106             */
107              
108 1309 100         parent = OpHAS_SIBLING(cUNOPo->op_first) ? cUNOPo : ((UNOP *)cUNOPo->op_first);
109 1309           prev = parent->op_first;
110 1309 50         invocant = OpSIBLING(prev);
111              
112 3717 50         for (cvop = invocant; OpHAS_SIBLING(cvop); cvop = OpSIBLING(cvop));
    100          
113              
114             /*
115             * now we have the CV OP, we can check if it's a method lookup.
116             * bail out if it's not
117             */
118 1309 100         if ((cvop->op_type != OP_METHOD) && (cvop->op_type != OP_METHOD_NAMED)) {
    100          
119 595           goto done;
120             }
121              
122             /* bail out if the invocant is a bareword e.g. Foo->bar */
123 714 100         if ((invocant->op_type == OP_CONST) && (invocant->op_private & OPpCONST_BARE)) {
    100          
124 20           goto done;
125             }
126              
127             /*
128             * the bareword flag is not set on the invocants of the `import`, `unimport`
129             * and `VERSION` methods faked up by `use` and `no` [1]. we have no other way
130             * to detect if an OP_CONST invocant is a bareword for these methods,
131             * so we have no choice but to assume it is and bail out so that we don't
132             * break `use`, `no` etc.
133             *
134             * (this is documented: the solution/workaround is to use
135             * $value->autobox_class instead.)
136             *
137             * [1] XXX this is a bug (in perl)
138             */
139 694 100         if (cvop->op_type == OP_METHOD_NAMED) {
140             /* SvPVX_const should be sane for the method name */
141 675           const char * method_name = SvPVX_const(((SVOP *)cvop)->op_sv);
142              
143 675 100         if (
144 642 100         strEQ(method_name, "import") ||
145 621 100         strEQ(method_name, "unimport") ||
146 621           strEQ(method_name, "VERSION")
147             ) {
148             goto done;
149             }
150             }
151              
152 633           hh = GvHV(PL_hintgv); /* the hints hash (%^H) */
153              
154             /* is there a bindings hashref for this scope? */
155 633           has_bindings = hh
156 633 50         && (svp = hv_fetch(hh, "autobox", 7, FALSE))
157 633 50         && *svp
158 1266 50         && SvROK(*svp);
    50          
159              
160 633 50         if (!has_bindings) {
161 0           goto done;
162             }
163              
164             /*
165             * if the invocant is an @array, %hash, @{ ... } or %{ ... }, then
166             * "auto-ref" it i.e. the optree equivalent of inserting a backslash
167             * before it:
168             *
169             * @foo->bar -> (\@foo)->bar
170             */
171 633 100         switch (invocant->op_type) {
172             case OP_PADAV:
173             case OP_PADHV:
174             case OP_RV2AV:
175             case OP_RV2HV:
176 25           auto_ref(aTHX_ invocant, parent, prev);
177             }
178              
179 633           cvop->op_flags |= OPf_SPECIAL;
180 1266           cvop->op_ppaddr = cvop->op_type == OP_METHOD
181             ? autobox_method
182 633 100         : autobox_method_named;
183              
184 633           PTABLE_store(AUTOBOX_OP_MAP, cvop, SvRV(*svp));
185              
186             done:
187 2554           return autobox_old_check_entersub(aTHX_ o);
188             }
189              
190 19           OP* autobox_method(pTHX) {
191 19           dVAR; dSP;
192 19           SV * const sv = TOPs;
193             SV * cv;
194              
195 19 100         if (SvROK(sv)) {
196 8           cv = SvRV(sv);
197              
198 8 50         if (SvTYPE(cv) == SVt_PVCV) {
199 8           SETs(cv);
200 8           RETURN;
201             }
202             }
203              
204 11           cv = autobox_method_common(aTHX_ sv, NULL);
205              
206 11 100         if (cv) {
207 8           SETs(cv);
208 8           RETURN;
209             } else {
210 3           return PL_ppaddr[OP_METHOD](aTHXR);
211             }
212             }
213              
214 604           OP* autobox_method_named(pTHX) {
215 604           dVAR; dSP;
216 604           SV * const sv = cSVOP_sv;
217 604           U32 hash = SvSHARED_HASH(sv);
218             SV * cv;
219              
220 604           cv = autobox_method_common(aTHX_ sv, &hash);
221              
222 604 100         if (cv) {
223 534 50         XPUSHs(cv);
224 534           RETURN;
225             } else {
226 604           return PL_ppaddr[OP_METHOD_NAMED](aTHXR);
227             }
228             }
229              
230             #define AUTOBOX_TYPE_RETURN(type) STMT_START { \
231             *len = (sizeof(type) - 1); return type; \
232             } STMT_END
233              
234 633           static const char *autobox_type(pTHX_ SV * const sv, STRLEN *len) {
235 633           switch (SvTYPE(sv)) {
236             case SVt_NULL:
237 0           AUTOBOX_TYPE_RETURN("UNDEF");
238             case SVt_IV:
239 110           AUTOBOX_TYPE_RETURN("INTEGER");
240             case SVt_PVIV:
241 19 100         if (SvIOK(sv)) {
242 16           AUTOBOX_TYPE_RETURN("INTEGER");
243             } else {
244 3           AUTOBOX_TYPE_RETURN("STRING");
245             }
246             case SVt_NV:
247 71 50         if (SvIOK(sv)) {
248 0           AUTOBOX_TYPE_RETURN("INTEGER");
249             } else {
250 71           AUTOBOX_TYPE_RETURN("FLOAT");
251             }
252             case SVt_PVNV:
253             /*
254             * integer before float:
255             * https://rt.cpan.org/Ticket/Display.html?id=46814
256             */
257 24 100         if (SvIOK(sv)) {
258 4           AUTOBOX_TYPE_RETURN("INTEGER");
259 20 100         } else if (SvNOK(sv)) {
260 17           AUTOBOX_TYPE_RETURN("FLOAT");
261             } else {
262 3           AUTOBOX_TYPE_RETURN("STRING");
263             }
264             #ifdef SVt_RV /* no longer defined by default if PERL_CORE is defined */
265             case SVt_RV:
266             #endif
267             case SVt_PV:
268             case SVt_PVMG:
269             #ifdef SvVOK
270 240 50         if (SvVOK(sv)) {
    0          
271 0           AUTOBOX_TYPE_RETURN("VSTRING");
272             }
273             #endif
274 240 50         if (SvROK(sv)) {
275 0           AUTOBOX_TYPE_RETURN("REF");
276             } else {
277 240           AUTOBOX_TYPE_RETURN("STRING");
278             }
279             case SVt_PVLV:
280 0 0         if (SvROK(sv)) {
281 0           AUTOBOX_TYPE_RETURN("REF");
282 0 0         } else if (LvTYPE(sv) == 't' || LvTYPE(sv) == 'T') { /* tied lvalue */
    0          
283 0 0         if (SvIOK(sv)) {
284 0           AUTOBOX_TYPE_RETURN("INTEGER");
285 0 0         } else if (SvNOK(sv)) {
286 0           AUTOBOX_TYPE_RETURN("FLOAT");
287             } else {
288 0           AUTOBOX_TYPE_RETURN("STRING");
289             }
290             } else {
291 0           AUTOBOX_TYPE_RETURN("LVALUE");
292             }
293             case SVt_PVAV:
294 62           AUTOBOX_TYPE_RETURN("ARRAY");
295             case SVt_PVHV:
296 50           AUTOBOX_TYPE_RETURN("HASH");
297             case SVt_PVCV:
298 57           AUTOBOX_TYPE_RETURN("CODE");
299             case SVt_PVGV:
300 0           AUTOBOX_TYPE_RETURN("GLOB");
301             case SVt_PVFM:
302 0           AUTOBOX_TYPE_RETURN("FORMAT");
303             case SVt_PVIO:
304 0           AUTOBOX_TYPE_RETURN("IO");
305             #ifdef SVt_BIND
306             case SVt_BIND:
307             AUTOBOX_TYPE_RETURN("BIND");
308             #endif
309             #ifdef SVt_REGEXP
310             case SVt_REGEXP:
311             AUTOBOX_TYPE_RETURN("REGEXP");
312             #endif
313             default:
314 0           AUTOBOX_TYPE_RETURN("UNKNOWN");
315             }
316             }
317              
318             /* returns either the method, or NULL, meaning delegate to the original op */
319             /* FIXME this has diverged from the implementation in pp_hot.c */
320 615           static SV * autobox_method_common(pTHX_ SV * method, U32* hashp) {
321 615           SV * const sv = *(PL_stack_base + TOPMARK + 1);
322              
323             /*
324             * if autobox is enabled (in scope) for this op and the invocant isn't
325             * an object...
326             */
327             /* don't use sv_isobject - we don't want to call SvGETMAGIC twice */
328 615 50         if ((PL_op->op_flags & OPf_SPECIAL) && ((!SvROK(sv)) || !SvOBJECT(SvRV(sv)))) {
    100          
    100          
329             HV * autobox_bindings;
330              
331 605 50         SvGETMAGIC(sv);
    0          
332              
333             /* this is the "bindings hash" that maps datatypes to package names */
334 605           autobox_bindings = (HV *)(PTABLE_fetch(AUTOBOX_OP_MAP, PL_op));
335              
336 605 50         if (autobox_bindings) {
337             const char * reftype; /* autobox_bindings key */
338             SV **svp; /* pointer to autobox_bindings value */
339 605           STRLEN typelen = 0;
340              
341             /*
342             * the type is either the invocant's reftype(), a subtype of
343             * SCALAR if it's not a ref, or UNDEF if it's not defined
344             */
345              
346 605 100         if (SvOK(sv)) {
    50          
    50          
347 581 100         reftype = autobox_type(aTHX_ (SvROK(sv) ? SvRV(sv) : sv), &typelen);
348             } else {
349 24           reftype = "UNDEF";
350 24           typelen = sizeof("UNDEF") - 1;
351             }
352              
353 605           svp = hv_fetch(autobox_bindings, reftype, typelen, 0);
354              
355 605 100         if (svp && SvOK(*svp)) {
    50          
    0          
    0          
356 545           SV * packsv = *svp;
357             STRLEN packlen;
358             HV * stash;
359             GV * gv;
360 545 50         const char * packname = SvPV_const(packsv, packlen);
361              
362 545           stash = gv_stashpvn(packname, packlen, FALSE);
363              
364 545 100         if (hashp) {
365             /* shortcut for simple names */
366 534           const HE* const he = hv_fetch_ent(stash, method, 0, *hashp);
367              
368 534 100         if (he) {
369 524           gv = (GV*)HeVAL(he);
370              
371             /*
372             * FIXME this has diverged from the implementation
373             * in pp_hot.c
374             */
375 524 100         if (
376 524           isGV(gv)
377 521 50         && GvCV(gv)
378 521 100         && (!GvCVGEN(gv) || GvCVGEN(gv) == PL_sub_generation)
    50          
379             ) {
380 605           return ((SV*)GvCV(gv));
381             }
382             }
383             }
384              
385             /*
386             * SvPV_nolen_const returns the method name as a const char *,
387             * stringifying names that are not strings (e.g. undef, SvIV,
388             * SvNV &c.) - see name.t
389             */
390 153 100         gv = gv_fetchmethod(
    50          
391             stash ? stash : (HV*)packsv,
392             SvPV_nolen_const(method)
393             );
394              
395 153 100         if (gv) {
396 153 50         return(isGV(gv) ? (SV*)GvCV(gv) : (SV*)gv);
397             }
398             }
399             }
400             }
401              
402 73           return NULL;
403             }
404              
405 20           static void autobox_cleanup(pTHX_ void * unused) {
406             PERL_UNUSED_VAR(unused); /* silence warning */
407              
408 20 50         if (AUTOBOX_OP_MAP) {
409 20           PTABLE_free(AUTOBOX_OP_MAP);
410 20           AUTOBOX_OP_MAP = NULL;
411             }
412 20           }
413              
414             MODULE = autobox PACKAGE = autobox
415              
416             PROTOTYPES: ENABLE
417              
418             BOOT:
419             /*
420             * XXX the BOOT section extends to the next blank line, so don't add one
421             * for readability
422             */
423 20           AUTOBOX_OP_MAP = PTABLE_new();
424 20 50         if (AUTOBOX_OP_MAP) {
425 20           Perl_call_atexit(aTHX_ autobox_cleanup, NULL);
426             } else {
427 0           Perl_croak(aTHX_ "Can't initialize OP map");
428             }
429              
430             void
431             _enter()
432             PROTOTYPE:
433             CODE:
434 77 100         if (AUTOBOX_SCOPE_DEPTH > 0) {
435 9           ++AUTOBOX_SCOPE_DEPTH;
436             } else {
437 68           AUTOBOX_SCOPE_DEPTH = 1;
438             /*
439             * capture the check routine in scope when autobox is used.
440             * usually, this will be Perl_ck_subr, though, in principle,
441             * it could be a bespoke checker spliced in by another module.
442             */
443 68           autobox_old_check_entersub = PL_check[OP_ENTERSUB];
444 68           PL_check[OP_ENTERSUB] = autobox_check_entersub;
445             }
446              
447             void
448             _leave()
449             PROTOTYPE:
450             CODE:
451 77 50         if (AUTOBOX_SCOPE_DEPTH == 0) {
452 0           Perl_warn(aTHX_ "scope underflow");
453             }
454              
455 77 100         if (AUTOBOX_SCOPE_DEPTH > 1) {
456 9           --AUTOBOX_SCOPE_DEPTH;
457             } else {
458 68           AUTOBOX_SCOPE_DEPTH = 0;
459 68           PL_check[OP_ENTERSUB] = autobox_old_check_entersub;
460             }
461              
462             void
463             _scope()
464             PROTOTYPE:
465             CODE:
466 102           XSRETURN_UV(PTR2UV(GvHV(PL_hintgv)));
467              
468             MODULE = autobox PACKAGE = autobox::universal
469              
470             SV *
471             type(SV * sv)
472             PROTOTYPE:$
473             PREINIT:
474 60           STRLEN len = 0;
475             const char *type;
476             CODE:
477 60 100         if (SvOK(sv)) {
    50          
    50          
478 52 100         type = autobox_type(aTHX_ (SvROK(sv) ? SvRV(sv) : sv), &len);
479 52           RETVAL = newSVpv(type, len);
480             } else {
481 8           RETVAL = newSVpv("UNDEF", sizeof("UNDEF") - 1);
482             }
483             OUTPUT:
484             RETVAL