File Coverage

lib/builtin.xs
Criterion Covered Total %
statement 248 277 89.5
branch 146 296 49.3
condition n/a
subroutine n/a
pod n/a
total 394 573 68.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, 2023 -- leonerd@leonerd.org.uk
5             */
6             #define PERL_NO_GET_CONTEXT
7              
8             #include "EXTERN.h"
9             #include "perl.h"
10             #include "XSUB.h"
11              
12             #define HAVE_PERL_VERSION(R, V, S) \
13             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
14              
15             #ifndef av_count
16             # define av_count(av) (AvFILL(av)+1)
17             #endif
18              
19             #ifndef intro_my
20             # define intro_my() Perl_intro_my(aTHX)
21             #endif
22              
23             #if !HAVE_PERL_VERSION(5, 38, 0)
24              
25             static U32 warning_offset;
26              
27             #define warn_experimental_builtin(name, prefix) S_warn_experimental_builtin(aTHX_ name, prefix)
28             static void S_warn_experimental_builtin(pTHX_ const char *name, bool prefix)
29             {
30             /* diag_listed_as: Built-in function '%s' is experimental */
31 49           Perl_ck_warner_d(aTHX_ packWARN(warning_offset),
32             "Built-in function '%s%s' is experimental",
33             prefix ? "builtin::" : "", name);
34             }
35              
36             #define prepare_export_lexical() S_prepare_export_lexical(aTHX)
37 31           static void S_prepare_export_lexical(pTHX)
38             {
39             assert(PL_compcv);
40              
41             /* We need to have PL_comppad / PL_curpad set correctly for lexical importing */
42 31           ENTER;
43 31           SAVESPTR(PL_comppad_name); PL_comppad_name = PadlistNAMES(CvPADLIST(PL_compcv));
44 31           SAVESPTR(PL_comppad); PL_comppad = PadlistARRAY(CvPADLIST(PL_compcv))[1];
45 31           SAVESPTR(PL_curpad); PL_curpad = PadARRAY(PL_comppad);
46 31           }
47              
48             #define export_lexical(name, sv) S_export_lexical(aTHX_ name, sv)
49 28           static void S_export_lexical(pTHX_ SV *name, SV *sv)
50             {
51 28 100         if(SvTYPE(sv) == SVt_PVCV && CvISXSUB(sv)) {
    50          
52             /* Before Perl v5.36, S_cv_clone() would crash on attempts to clone a
53             * CV containing a lexically exported XSUB.
54             *
55             * See also
56             * https://github.com/Perl/perl5/pull/19232/files#diff-d6972c2c727b9f7dfb3dc6c58950ad9e884aeaa7464c1dfe70ed0c7512719e7fR2212-R2226
57             */
58 0 0         croak("Cannot lexically export an XSUB as %s on this version of perl", SvPVbyte_nolen(name));
59             }
60             else
61             SvREFCNT_inc(sv);
62              
63 28           PADOFFSET off = pad_add_name_sv(name, padadd_STATE, 0, 0);
64 28           SvREFCNT_dec(PL_curpad[off]);
65 28           PL_curpad[off] = sv;
66 28           }
67              
68             #define finish_export_lexical() S_finish_export_lexical(aTHX)
69             static void S_finish_export_lexical(pTHX)
70             {
71 31           intro_my();
72              
73 31           LEAVE;
74             }
75              
76 2           OP *pp_builtin_export_lexically(pTHX)
77             {
78 2           dSP;
79 2 50         U32 items = av_count(GvAV(PL_defgv));
80             warn_experimental_builtin("export_lexically", true);
81              
82 2 50         if(!PL_compcv)
83 0           Perl_croak(aTHX_
84             "export_lexically can only be called at compile time");
85              
86 2 50         if(items % 2)
87 0           Perl_croak(aTHX_ "Odd number of elements in export_lexically");
88              
89 2           SP -= items;
90 2           SV **args = SP + 1;
91              
92 6 100         for(int i = 0; i < items; i += 2) {
93 4           SV *name = args[i];
94 4           SV *ref = args[i+1];
95              
96 4 50         if(!SvROK(ref))
97             /* diag_listed_as: Expected %s reference in export_lexically */
98 0           Perl_croak(aTHX_ "Expected a reference in export_lexically");
99              
100 4           char sigil = SvPVX(name)[0];
101 4           SV *rv = SvRV(ref);
102              
103             const char *bad = NULL;
104 4           switch(sigil) {
105             default:
106             /* overwrites the pointer on the stack; but this is fine, the
107             * caller's value isn't modified */
108 1           args[i] = name = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(name)));
109              
110             /* FALLTHROUGH */
111             case '&':
112 1 50         if(SvTYPE(rv) != SVt_PVCV)
113             bad = "a CODE";
114             break;
115              
116             case '$':
117             /* Permit any of SVt_NULL to SVt_PVMG. Technically this also
118             * includes SVt_INVLIST but it isn't thought possible for pureperl
119             * code to ever manage to see one of those. */
120 1 50         if(SvTYPE(rv) > SVt_PVMG)
121             bad = "a SCALAR";
122             break;
123              
124             case '@':
125 1 50         if(SvTYPE(rv) != SVt_PVAV)
126             bad = "an ARRAY";
127             break;
128              
129             case '%':
130 1 50         if(SvTYPE(rv) != SVt_PVHV)
131             bad = "a HASH";
132             break;
133             }
134              
135 4 50         if(bad)
136 0           Perl_croak(aTHX_ "Expected %s reference in export_lexically", bad);
137             }
138              
139 2           prepare_export_lexical();
140              
141 6 100         for(int i = 0; i < items; i += 2) {
142 4           SV *name = args[i];
143 4           SV *ref = args[i+1];
144              
145 4           export_lexical(name, SvRV(ref));
146             }
147              
148             finish_export_lexical();
149              
150 2           RETURN;
151             }
152              
153 4           OP *pp_builtin_is_tainted(pTHX)
154             {
155 4           dSP;
156 4 50         U32 items = av_count(GvAV(PL_defgv));
157             warn_experimental_builtin("is_tainted", true);
158 4 50         if(items != 1)
159 0           croak_xs_usage(find_runcv(0), "arg");
160              
161 4           SV *arg = POPs;
162 4 50         SvGETMAGIC(arg);
163 4 50         PUSHs(boolSV(SvTAINTED(arg)));
    100          
164              
165 4           RETURN;
166             }
167             #endif /* !HAVE_PERL_VERSION(5, 38, 0) */
168              
169             #if !HAVE_PERL_VERSION(5, 36, 0)
170              
171             #define G_LIST G_ARRAY
172              
173             /* Perl v5.36 added the 'scalar' warning category; before that such warnings
174             * appeared in 'void' */
175             #define WARN_SCALAR WARN_VOID
176              
177             #include
178              
179             #ifndef isSPACE_utf8_safe
180             # define isSPACE_utf8_safe(start, end) isSPACE_utf8(start)
181             #endif
182              
183             #define report_uninit(sv) Perl_report_uninit(aTHX_ sv)
184              
185             #if !HAVE_PERL_VERSION(5, 28, 0)
186             # define sv_rvunweaken(sv) S_sv_rvunweaken(aTHX_ sv)
187 1           static void S_sv_rvunweaken(pTHX_ SV *sv)
188             {
189 1 50         if(!SvOK(sv))
    0          
    0          
190             return;
191 1 50         if(!SvROK(sv))
192 0           croak("Can't unweaken a nonreference");
193 1 50         else if(!SvWEAKREF(sv)) {
194 0 0         if(ckWARN(WARN_MISC))
195 0           warn("Reference is not weak");
196             return;
197             }
198 1 50         else if(SvREADONLY(sv))
199 0           croak_no_modify();
200              
201 1           SV *tsv = SvRV(sv);
202 1           SvWEAKREF_off(sv);
203 1           SvROK_on(sv);
204             SvREFCNT_inc_NN(tsv);
205 1           Perl_sv_del_backref(aTHX_ tsv, sv);
206             }
207             #endif
208              
209             #if !HAVE_PERL_VERSION(5, 24, 0)
210             # ifndef sv_sethek
211             # define sv_sethek(a, b) Perl_sv_sethek(aTHX_ a, b)
212             # endif
213              
214             # define sv_ref(dst, sv, ob) S_sv_ref(aTHX_ dst, sv, ob)
215             static SV *S_sv_ref(pTHX_ SV *dst, SV *sv, int ob)
216             {
217             /* copied from perl 5.22's sv.c */
218             if(!dst)
219             dst = sv_newmortal();
220              
221             if(ob && SvOBJECT(sv)) {
222             if(HvNAME_get(SvSTASH(sv)))
223             sv_sethek(dst, HvNAME_HEK(SvSTASH(sv)));
224             else
225             sv_setpvs(dst, "__ANON__");
226             }
227             else {
228             const char *reftype = sv_reftype(sv, 0);
229             sv_setpv(dst, reftype);
230             }
231              
232             return dst;
233             }
234             #endif
235              
236 4           OP *pp_builtin_blessed(pTHX)
237             {
238 4           dSP;
239 4 50         U32 items = av_count(GvAV(PL_defgv));
240             warn_experimental_builtin("blessed", true);
241 4 50         if(items != 1)
242 0           croak_xs_usage(find_runcv(0), "arg");
243              
244 4           SV *arg = POPs;
245 4 50         SvGETMAGIC(arg);
246 4 50         if(!SvROK(arg) || !SvOBJECT(SvRV(arg)))
    100          
247 1           PUSHs(&PL_sv_undef);
248             else
249 3           PUSHs(sv_mortalcopy(sv_ref(NULL, SvRV(arg), TRUE)));
250              
251 4           RETURN;
252             }
253              
254 3           OP *pp_builtin_ceil(pTHX)
255             {
256 3           dSP;
257 3 50         U32 items = av_count(GvAV(PL_defgv));
258             warn_experimental_builtin("ceil", true);
259 3 50         if(items != 1)
260 0           croak_xs_usage(find_runcv(0), "arg");
261              
262 3           SV *arg = POPs;
263 3 100         mPUSHn(Perl_ceil(SvNV(arg)));
264              
265 3           RETURN;
266             }
267              
268 2           OP *pp_builtin_false(pTHX)
269             {
270 2           dSP;
271 2 50         U32 items = av_count(GvAV(PL_defgv));
272             warn_experimental_builtin("false", true);
273 2 50         if(items)
274 0           croak_xs_usage(find_runcv(0), "");
275              
276 2 50         XPUSHs(&PL_sv_no);
277              
278 2           RETURN;
279             }
280              
281 3           OP *pp_builtin_floor(pTHX)
282             {
283 3           dSP;
284 3 50         U32 items = av_count(GvAV(PL_defgv));
285             warn_experimental_builtin("floor", true);
286 3 50         if(items != 1)
287 0           croak_xs_usage(find_runcv(0), "arg");
288              
289 3           SV *arg = POPs;
290 3 100         mPUSHn(Perl_floor(SvNV(arg)));
291              
292 3           RETURN;
293             }
294              
295 6           OP *pp_builtin_indexed(pTHX)
296             {
297 6           dSP;
298 6 50         U32 items = av_count(GvAV(PL_defgv));
299              
300 6           SP -= items;
301              
302 6 50         switch(GIMME_V) {
303             case G_VOID:
304 0           Perl_ck_warner(aTHX_ packWARN(WARN_VOID),
305             "Useless use of %s in void context", "builtin::indexed");
306 0           RETURN;
307              
308             case G_SCALAR:
309 1           Perl_ck_warner(aTHX_ packWARN(WARN_SCALAR),
310             "Useless use of %s in scalar context", "builtin::indexed");
311 1           mPUSHi(items * 2);
312              
313 1           RETURN;
314              
315             case G_LIST:
316             break;
317             }
318              
319 5           SSize_t retcount = items * 2;
320 5 50         EXTEND(SP, retcount);
321              
322 5           SV **stack = SP + 1;
323              
324             /* Copy from [items-1] down to [0] so we don't have to make
325             * temporary copies */
326 17 100         for(SSize_t index = (SSize_t)items - 1; index >= 0; index--) {
327             /* Copy, not alias */
328 12           stack[index * 2 + 1] = sv_mortalcopy(stack[index]);
329 12           stack[index * 2] = sv_2mortal(newSViv(index));
330             }
331              
332 5           SP += retcount;
333              
334 5           RETURN;
335             }
336              
337 3           OP *pp_builtin_is_weak(pTHX)
338             {
339 3           dSP;
340 3 50         U32 items = av_count(GvAV(PL_defgv));
341             warn_experimental_builtin("is_weak", true);
342 3 50         if(items != 1)
343 0           croak_xs_usage(find_runcv(0), "arg");
344              
345 3           SV *arg = POPs;
346 3 100         PUSHs(boolSV(SvROK(arg) && SvWEAKREF(arg)));
347              
348 3           RETURN;
349             }
350              
351 2           OP *pp_builtin_refaddr(pTHX)
352             {
353 2           dSP;
354 2 50         U32 items = av_count(GvAV(PL_defgv));
355             warn_experimental_builtin("refaddr", true);
356 2 50         if(items != 1)
357 0           croak_xs_usage(find_runcv(0), "arg");
358              
359 2           SV *arg = POPs;
360 2 50         SvGETMAGIC(arg);
361 2 100         if(!SvROK(arg))
362 1           PUSHs(&PL_sv_undef);
363             else
364 1           mPUSHu(PTR2UV(SvRV(arg)));
365              
366 2           RETURN;
367             }
368              
369 3           OP *pp_builtin_reftype(pTHX)
370             {
371 3           dSP;
372 3 50         U32 items = av_count(GvAV(PL_defgv));
373             warn_experimental_builtin("reftype", true);
374 3 50         if(items != 1)
375 0           croak_xs_usage(find_runcv(0), "arg");
376              
377 3           SV *arg = POPs;
378 3 50         SvGETMAGIC(arg);
379 3 100         if(!SvROK(arg))
380 1           PUSHs(&PL_sv_undef);
381             else
382 2           PUSHs(sv_2mortal(newSVpv(sv_reftype(SvRV(arg), FALSE), 0)));
383              
384 3           RETURN;
385             }
386              
387             static XOP xop_builtin_trim;
388 13           OP *pp_builtin_trim(pTHX)
389             {
390 13           dSP;
391 13 50         U32 items = av_count(GvAV(PL_defgv));
392             warn_experimental_builtin("trim", true);
393 13 50         if(items != 1)
394 0           croak_xs_usage(find_runcv(0), "arg");
395              
396 13           SV *source = TOPs;
397              
398             STRLEN len;
399             const U8 *start;
400             SV *dest;
401              
402 13 100         SvGETMAGIC(source);
403              
404 13 100         if (SvOK(source))
    50          
    50          
405 12 100         start = (const U8*)SvPV_nomg_const(source, len);
406             else {
407 1 50         if (ckWARN(WARN_UNINITIALIZED))
408 1           report_uninit(source);
409             start = (const U8*)"";
410 1           len = 0;
411             }
412              
413 15 100         if (DO_UTF8(source)) {
    50          
414 2           const U8 *end = start + len;
415              
416             /* Find the first non-space */
417 6 50         while(len) {
418             STRLEN thislen;
419 6 100         if (!isSPACE_utf8_safe(start, end))
    50          
    0          
    0          
    50          
    50          
    0          
    0          
    50          
    100          
    50          
    50          
    0          
    50          
    0          
    0          
    0          
    0          
    100          
420             break;
421 4           start += (thislen = UTF8SKIP(start));
422 4           len -= thislen;
423             }
424              
425             /* Find the final non-space */
426             STRLEN thislen;
427             const U8 *cur_end = end;
428 11 50         while ((thislen = is_SPACE_utf8_safe_backwards(cur_end, start))) {
    100          
    50          
    0          
    0          
    0          
    0          
    100          
    100          
    50          
    100          
    50          
    50          
    0          
    0          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
429 9           cur_end -= thislen;
430             }
431 2           len -= (end - cur_end);
432             }
433 11 100         else if (len) {
434 30 50         while(len) {
435 30 100         if (!isSPACE_L1(*start))
436             break;
437 21           start++;
438 21           len--;
439             }
440              
441 27 50         while(len) {
442 27 100         if (!isSPACE_L1(start[len-1]))
443             break;
444 18           len--;
445             }
446             }
447              
448 13           dest = sv_newmortal();
449              
450 13 50         if (SvPOK(dest) && (dest == source)) {
    0          
451 0           sv_chop(dest, (const char *)start);
452 0           SvCUR_set(dest, len);
453             }
454             else {
455 13 50         SvUPGRADE(dest, SVt_PV);
456 13 50         SvGROW(dest, len + 1);
    50          
457              
458 13           Copy(start, SvPVX(dest), len, U8);
459 13           SvPVX(dest)[len] = '\0';
460 13           SvPOK_on(dest);
461 13           SvCUR_set(dest, len);
462              
463 13 100         if (DO_UTF8(source))
    50          
464 2           SvUTF8_on(dest);
465             else
466 11           SvUTF8_off(dest);
467              
468 13 100         if (SvTAINTED(source))
    50          
469 0 0         SvTAINT(dest);
    0          
470             }
471              
472 13 50         SvSETMAGIC(dest);
473 13           TOPs = dest;
474              
475 13           RETURN;
476             }
477              
478 7           OP *pp_builtin_true(pTHX)
479             {
480 7           dSP;
481 7 50         U32 items = av_count(GvAV(PL_defgv));
482             warn_experimental_builtin("true", true);
483 7 50         if(items)
484 0           croak_xs_usage(find_runcv(0), "");
485              
486 7 50         XPUSHs(&PL_sv_yes);
487              
488 7           RETURN;
489             }
490              
491 1           OP *pp_builtin_unweaken(pTHX)
492             {
493 1           dSP;
494 1 50         U32 items = av_count(GvAV(PL_defgv));
495             warn_experimental_builtin("weaken", true);
496 1 50         if(items != 1)
497 0           croak_xs_usage(find_runcv(0), "arg");
498              
499 1           SV *arg = POPs;
500 1           sv_rvunweaken(arg);
501              
502 1           RETURN;
503             }
504              
505 2           OP *pp_builtin_weaken(pTHX)
506             {
507 2           dSP;
508 2 50         U32 items = av_count(GvAV(PL_defgv));
509             warn_experimental_builtin("weaken", true);
510 2 50         if(items != 1)
511 0           croak_xs_usage(find_runcv(0), "arg");
512              
513 2           SV *arg = POPs;
514 2           sv_rvweaken(arg);
515              
516 2           RETURN;
517             }
518              
519             static const char builtin_not_recognised[] = "'%" SVf "' is not recognised as a builtin function";
520              
521             XS(XS_builtin_import);
522 29           XS(XS_builtin_import)
523             {
524 58           dXSARGS;
525              
526 29 50         if(!PL_compcv)
527 0           Perl_croak(aTHX_
528             "builtin::import can only be called at compile time");
529              
530 29           prepare_export_lexical();
531              
532 53 100         for(int i = 1; i < items; i++) {
533 24           SV *sym = ST(i);
534 24 50         if(strEQ(SvPV_nolen(sym), "import"))
    50          
535 0           Perl_croak(aTHX_ builtin_not_recognised, sym);
536              
537 24           SV *ampname = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(sym)));
538 24           SV *fqname = sv_2mortal(Perl_newSVpvf(aTHX_ "builtin::%" SVf, SVfARG(sym)));
539              
540 24 50         CV *cv = get_cv(SvPV_nolen(fqname), SvUTF8(fqname) ? SVf_UTF8 : 0);
541 24 50         if(!cv)
542 0           Perl_croak(aTHX_ builtin_not_recognised, sym);
543              
544 24           export_lexical(ampname, (SV *)cv);
545             }
546              
547             finish_export_lexical();
548 29           }
549              
550             #endif /* !HAVE_PERL_VERSION(5, 36, 0) */
551              
552             #define newCUSTOMOP_SUB(name, proto, ppfunc) S_newCUSTOMOP_SUB(aTHX_ name, proto, ppfunc)
553 154           static CV *S_newCUSTOMOP_SUB(pTHX_ const char *name, const char *proto, OP *(*ppfunc)(pTHX))
554             {
555 154           I32 floor_ix = start_subparse(FALSE, 0);
556              
557 154           OP *body = newOP(OP_CUSTOM, 0);
558 154           body->op_ppaddr = ppfunc;
559              
560 154           OP *nameop = newSVOP(OP_CONST, 0, newSVpv(name, 0));
561             OP *protoop = NULL;
562 154 100         if(proto)
563 132           protoop = newSVOP(OP_CONST, 0, newSVpv(proto, 0));
564 154           CV *cv = newATTRSUB(floor_ix, nameop, protoop, NULL, body);
565 154           }
566              
567             MODULE = builtin PACKAGE = builtin
568              
569             BOOT:
570             #if !HAVE_PERL_VERSION(5, 38, 0)
571             {
572             CV *trim_cv;
573             # if HAVE_PERL_VERSION(5, 36, 0)
574             trim_cv = get_cv("builtin::trim", 0);
575             # else
576 11           trim_cv = newCUSTOMOP_SUB("builtin::trim", "$", &pp_builtin_trim);
577 11           XopENTRY_set(&xop_builtin_trim, xop_name, "trim");
578 11           XopENTRY_set(&xop_builtin_trim, xop_desc, "trim");
579 11           Perl_custom_op_register(aTHX_ &pp_builtin_trim, &xop_builtin_trim);
580             # endif
581             assert(trim_cv);
582              
583             /* prototype is stored directly in the PV slot */
584 11           sv_setpv((SV *)trim_cv, "$");
585             }
586              
587 11           newCUSTOMOP_SUB("builtin::is_tainted", "$", &pp_builtin_is_tainted);
588 11           newCUSTOMOP_SUB("builtin::export_lexically", NULL, &pp_builtin_export_lexically);
589             #endif
590             #if HAVE_PERL_VERSION(5, 36, 0)
591             warning_offset = WARN_EXPERIMENTAL__BUILTIN;
592             #else
593             {
594             HV *offsets_hv;
595             SV **svp;
596              
597 11           dSP;
598              
599 11           ENTER;
600 11           SAVETMPS;
601 11 50         EXTEND(SP, 1);
602              
603 11 50         PUSHMARK(SP);
604 11           mPUSHp("experimental::builtin", 21);
605 11           PUTBACK;
606              
607 11           call_pv("warnings::register_categories", G_VOID);
608              
609 11 50         FREETMPS;
610 11           LEAVE;
611              
612 11           offsets_hv = get_hv("warnings::Offsets", 0);
613             assert(offsets_hv);
614              
615 11           svp = hv_fetchs(offsets_hv, "experimental::builtin", 0);
616             assert(svp);
617             assert(*svp);
618              
619 11 50         warning_offset = SvUV(*svp) / 2;
620             }
621              
622 11           newCUSTOMOP_SUB("builtin::blessed", "$", &pp_builtin_blessed);
623 11           newCUSTOMOP_SUB("builtin::ceil", "$", &pp_builtin_ceil);
624 11           newCUSTOMOP_SUB("builtin::false", "", &pp_builtin_false);
625 11           newCUSTOMOP_SUB("builtin::floor", "$", &pp_builtin_floor);
626 11           newCUSTOMOP_SUB("builtin::indexed", NULL, &pp_builtin_indexed);
627 11           newCUSTOMOP_SUB("builtin::is_weak", "$", &pp_builtin_is_weak);
628 11           newCUSTOMOP_SUB("builtin::refaddr", "$", &pp_builtin_refaddr);
629 11           newCUSTOMOP_SUB("builtin::reftype", "$", &pp_builtin_reftype);
630 11           newCUSTOMOP_SUB("builtin::true", "", &pp_builtin_true);
631 11           newCUSTOMOP_SUB("builtin::unweaken", "$", &pp_builtin_unweaken);
632 11           newCUSTOMOP_SUB("builtin::weaken", "$", &pp_builtin_weaken);
633              
634 11           newXS_flags("builtin::import", &XS_builtin_import, __FILE__, NULL, 0);
635             #endif