Commit | Line | Data |
---|---|---|
6a2e756f PE |
1 | /* builtin.c |
2 | * | |
3 | * Copyright (C) 2021 by Paul Evans and others | |
4 | * | |
5 | * You may distribute under the terms of either the GNU General Public | |
6 | * License or the Artistic License, as specified in the README file. | |
7 | * | |
8 | */ | |
9 | ||
10 | /* This file contains the code that implements functions in perl's "builtin::" | |
11 | * namespace | |
12 | */ | |
13 | ||
14 | #include "EXTERN.h" | |
15 | #include "perl.h" | |
16 | ||
17 | #include "XSUB.h" | |
18 | ||
5a94e094 PE |
19 | /* copied from op.c */ |
20 | #define SHORTVER(maj,min) (((maj) << 8) | (min)) | |
21 | ||
852c1a84 PE |
22 | struct BuiltinFuncDescriptor { |
23 | const char *name; | |
5a94e094 | 24 | U16 since_ver; |
852c1a84 PE |
25 | XSUBADDR_t xsub; |
26 | OP *(*checker)(pTHX_ OP *, GV *, SV *); | |
27 | IV ckval; | |
edd58c23 | 28 | bool is_experimental; |
852c1a84 PE |
29 | }; |
30 | ||
d0364e1b PE |
31 | #define warn_experimental_builtin(name) S_warn_experimental_builtin(aTHX_ name) |
32 | static void S_warn_experimental_builtin(pTHX_ const char *name) | |
929db505 DIM |
33 | { |
34 | /* diag_listed_as: Built-in function '%s' is experimental */ | |
35 | Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__BUILTIN), | |
d0364e1b | 36 | "Built-in function 'builtin::%s' is experimental", name); |
929db505 DIM |
37 | } |
38 | ||
bb0dc1a9 PE |
39 | /* These three utilities might want to live elsewhere to be reused from other |
40 | * code sometime | |
41 | */ | |
42 | #define prepare_export_lexical() S_prepare_export_lexical(aTHX) | |
43 | static void S_prepare_export_lexical(pTHX) | |
44 | { | |
45 | assert(PL_compcv); | |
46 | ||
47 | /* We need to have PL_comppad / PL_curpad set correctly for lexical importing */ | |
48 | ENTER; | |
49 | SAVESPTR(PL_comppad_name); PL_comppad_name = PadlistNAMES(CvPADLIST(PL_compcv)); | |
50 | SAVESPTR(PL_comppad); PL_comppad = PadlistARRAY(CvPADLIST(PL_compcv))[1]; | |
51 | SAVESPTR(PL_curpad); PL_curpad = PadARRAY(PL_comppad); | |
52 | } | |
53 | ||
54 | #define export_lexical(name, sv) S_export_lexical(aTHX_ name, sv) | |
55 | static void S_export_lexical(pTHX_ SV *name, SV *sv) | |
56 | { | |
57 | PADOFFSET off = pad_add_name_sv(name, padadd_STATE, 0, 0); | |
58 | SvREFCNT_dec(PL_curpad[off]); | |
59 | PL_curpad[off] = SvREFCNT_inc(sv); | |
60 | } | |
61 | ||
62 | #define finish_export_lexical() S_finish_export_lexical(aTHX) | |
63 | static void S_finish_export_lexical(pTHX) | |
64 | { | |
65 | intro_my(); | |
66 | ||
67 | LEAVE; | |
68 | } | |
69 | ||
70 | ||
6a2e756f PE |
71 | XS(XS_builtin_true); |
72 | XS(XS_builtin_true) | |
73 | { | |
74 | dXSARGS; | |
75 | if(items) | |
76 | croak_xs_usage(cv, ""); | |
77 | XSRETURN_YES; | |
78 | } | |
79 | ||
80 | XS(XS_builtin_false); | |
81 | XS(XS_builtin_false) | |
82 | { | |
83 | dXSARGS; | |
84 | if(items) | |
85 | croak_xs_usage(cv, ""); | |
86 | XSRETURN_NO; | |
87 | } | |
88 | ||
852c1a84 PE |
89 | enum { |
90 | BUILTIN_CONST_FALSE, | |
91 | BUILTIN_CONST_TRUE, | |
92 | }; | |
93 | ||
94 | static OP *ck_builtin_const(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) | |
95 | { | |
96 | const struct BuiltinFuncDescriptor *builtin = NUM2PTR(const struct BuiltinFuncDescriptor *, SvUV(ckobj)); | |
97 | ||
98 | SV *prototype = newSVpvs(""); | |
99 | SAVEFREESV(prototype); | |
100 | ||
101 | assert(entersubop->op_type == OP_ENTERSUB); | |
102 | ||
103 | entersubop = ck_entersub_args_proto(entersubop, namegv, prototype); | |
104 | ||
105 | SV *constval; | |
106 | switch(builtin->ckval) { | |
107 | case BUILTIN_CONST_FALSE: constval = &PL_sv_no; break; | |
108 | case BUILTIN_CONST_TRUE: constval = &PL_sv_yes; break; | |
109 | default: | |
c0e63b13 KW |
110 | DIE(aTHX_ "panic: unrecognised builtin_const value %" IVdf, |
111 | builtin->ckval); | |
852c1a84 PE |
112 | break; |
113 | } | |
114 | ||
115 | op_free(entersubop); | |
116 | ||
117 | return newSVOP(OP_CONST, 0, constval); | |
118 | } | |
119 | ||
6ac93b49 PE |
120 | XS(XS_builtin_func1_scalar); |
121 | XS(XS_builtin_func1_scalar) | |
6a2e756f PE |
122 | { |
123 | dXSARGS; | |
852c1a84 PE |
124 | dXSI32; |
125 | ||
6a2e756f | 126 | if(items != 1) |
852c1a84 PE |
127 | croak_xs_usage(cv, "arg"); |
128 | ||
129 | switch(ix) { | |
5a94615f | 130 | case OP_IS_BOOL: |
d0364e1b | 131 | warn_experimental_builtin(PL_op_name[ix]); |
5a94615f | 132 | Perl_pp_is_bool(aTHX); |
852c1a84 | 133 | break; |
6a2e756f | 134 | |
5a94615f PE |
135 | case OP_IS_WEAK: |
136 | Perl_pp_is_weak(aTHX); | |
6ac93b49 PE |
137 | break; |
138 | ||
d2817bd7 PE |
139 | case OP_BLESSED: |
140 | Perl_pp_blessed(aTHX); | |
141 | break; | |
142 | ||
143 | case OP_REFADDR: | |
144 | Perl_pp_refaddr(aTHX); | |
145 | break; | |
146 | ||
147 | case OP_REFTYPE: | |
148 | Perl_pp_reftype(aTHX); | |
149 | break; | |
150 | ||
17a8df70 JR |
151 | case OP_CEIL: |
152 | Perl_pp_ceil(aTHX); | |
153 | break; | |
154 | ||
155 | case OP_FLOOR: | |
156 | Perl_pp_floor(aTHX); | |
157 | break; | |
158 | ||
a02b8151 JR |
159 | case OP_IS_TAINTED: |
160 | Perl_pp_is_tainted(aTHX); | |
161 | break; | |
162 | ||
7c60f1f7 PE |
163 | case OP_STRINGIFY: |
164 | Perl_pp_stringify(aTHX); | |
165 | break; | |
166 | ||
852c1a84 | 167 | default: |
c0e63b13 | 168 | Perl_die(aTHX_ "panic: unhandled opcode %" IVdf |
730f927d | 169 | " for xs_builtin_func1_scalar()", (IV) ix); |
852c1a84 PE |
170 | } |
171 | ||
172 | XSRETURN(1); | |
173 | } | |
174 | ||
42a429a3 KW |
175 | XS(XS_builtin_trim); |
176 | XS(XS_builtin_trim) | |
177 | { | |
178 | dXSARGS; | |
179 | ||
42a429a3 KW |
180 | if (items != 1) { |
181 | croak_xs_usage(cv, "arg"); | |
182 | } | |
183 | ||
184 | dTARGET; | |
185 | SV *source = TOPs; | |
186 | STRLEN len; | |
187 | const U8 *start; | |
188 | SV *dest; | |
189 | ||
190 | SvGETMAGIC(source); | |
191 | ||
192 | if (SvOK(source)) | |
193 | start = (const U8*)SvPV_nomg_const(source, len); | |
194 | else { | |
195 | if (ckWARN(WARN_UNINITIALIZED)) | |
196 | report_uninit(source); | |
197 | start = (const U8*)""; | |
198 | len = 0; | |
199 | } | |
200 | ||
201 | if (DO_UTF8(source)) { | |
202 | const U8 *end = start + len; | |
203 | ||
204 | /* Find the first non-space */ | |
205 | while(len) { | |
206 | STRLEN thislen; | |
207 | if (!isSPACE_utf8_safe(start, end)) | |
208 | break; | |
209 | start += (thislen = UTF8SKIP(start)); | |
210 | len -= thislen; | |
211 | } | |
212 | ||
213 | /* Find the final non-space */ | |
214 | STRLEN thislen; | |
215 | const U8 *cur_end = end; | |
216 | while ((thislen = is_SPACE_utf8_safe_backwards(cur_end, start))) { | |
217 | cur_end -= thislen; | |
218 | } | |
219 | len -= (end - cur_end); | |
220 | } | |
221 | else if (len) { | |
222 | while(len) { | |
223 | if (!isSPACE_L1(*start)) | |
224 | break; | |
225 | start++; | |
226 | len--; | |
227 | } | |
228 | ||
229 | while(len) { | |
230 | if (!isSPACE_L1(start[len-1])) | |
231 | break; | |
232 | len--; | |
233 | } | |
234 | } | |
235 | ||
236 | dest = TARG; | |
237 | ||
238 | if (SvPOK(dest) && (dest == source)) { | |
239 | sv_chop(dest, (const char *)start); | |
240 | SvCUR_set(dest, len); | |
241 | } | |
242 | else { | |
243 | SvUPGRADE(dest, SVt_PV); | |
244 | SvGROW(dest, len + 1); | |
245 | ||
246 | Copy(start, SvPVX(dest), len, U8); | |
247 | SvPVX(dest)[len] = '\0'; | |
248 | SvPOK_on(dest); | |
249 | SvCUR_set(dest, len); | |
250 | ||
251 | if (DO_UTF8(source)) | |
252 | SvUTF8_on(dest); | |
253 | else | |
254 | SvUTF8_off(dest); | |
255 | ||
256 | if (SvTAINTED(source)) | |
257 | SvTAINT(dest); | |
258 | } | |
259 | ||
260 | SvSETMAGIC(dest); | |
261 | ||
262 | SETs(dest); | |
263 | ||
264 | XSRETURN(1); | |
265 | } | |
266 | ||
f846dd12 PE |
267 | XS(XS_builtin_export_lexically); |
268 | XS(XS_builtin_export_lexically) | |
269 | { | |
270 | dXSARGS; | |
271 | ||
d0364e1b | 272 | warn_experimental_builtin("export_lexically"); |
f846dd12 PE |
273 | |
274 | if(!PL_compcv) | |
275 | Perl_croak(aTHX_ | |
276 | "export_lexically can only be called at compile time"); | |
277 | ||
278 | if(items % 2) | |
279 | Perl_croak(aTHX_ "Odd number of elements in export_lexically"); | |
280 | ||
281 | for(int i = 0; i < items; i += 2) { | |
282 | SV *name = ST(i); | |
283 | SV *ref = ST(i+1); | |
284 | ||
285 | if(!SvROK(ref)) | |
286 | /* diag_listed_as: Expected %s reference in export_lexically */ | |
287 | Perl_croak(aTHX_ "Expected a reference in export_lexically"); | |
288 | ||
289 | char sigil = SvPVX(name)[0]; | |
290 | SV *rv = SvRV(ref); | |
291 | ||
292 | const char *bad = NULL; | |
293 | switch(sigil) { | |
294 | default: | |
295 | /* overwrites the pointer on the stack; but this is fine, the | |
296 | * caller's value isn't modified */ | |
297 | ST(i) = name = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(name))); | |
298 | ||
299 | /* FALLTHROUGH */ | |
300 | case '&': | |
301 | if(SvTYPE(rv) != SVt_PVCV) | |
302 | bad = "a CODE"; | |
303 | break; | |
304 | ||
305 | case '$': | |
306 | /* Permit any of SVt_NULL to SVt_PVMG. Technically this also | |
307 | * includes SVt_INVLIST but it isn't thought possible for pureperl | |
308 | * code to ever manage to see one of those. */ | |
309 | if(SvTYPE(rv) > SVt_PVMG) | |
310 | bad = "a SCALAR"; | |
311 | break; | |
312 | ||
313 | case '@': | |
314 | if(SvTYPE(rv) != SVt_PVAV) | |
315 | bad = "an ARRAY"; | |
316 | break; | |
317 | ||
318 | case '%': | |
319 | if(SvTYPE(rv) != SVt_PVHV) | |
320 | bad = "a HASH"; | |
321 | break; | |
322 | } | |
323 | ||
324 | if(bad) | |
325 | Perl_croak(aTHX_ "Expected %s reference in export_lexically", bad); | |
326 | } | |
327 | ||
328 | prepare_export_lexical(); | |
329 | ||
330 | for(int i = 0; i < items; i += 2) { | |
331 | SV *name = ST(i); | |
332 | SV *ref = ST(i+1); | |
333 | ||
334 | export_lexical(name, SvRV(ref)); | |
335 | } | |
336 | ||
337 | finish_export_lexical(); | |
338 | } | |
339 | ||
6ac93b49 PE |
340 | XS(XS_builtin_func1_void); |
341 | XS(XS_builtin_func1_void) | |
342 | { | |
343 | dXSARGS; | |
344 | dXSI32; | |
345 | ||
346 | if(items != 1) | |
347 | croak_xs_usage(cv, "arg"); | |
348 | ||
349 | switch(ix) { | |
350 | case OP_WEAKEN: | |
351 | Perl_pp_weaken(aTHX); | |
352 | break; | |
353 | ||
354 | case OP_UNWEAKEN: | |
355 | Perl_pp_unweaken(aTHX); | |
356 | break; | |
357 | ||
358 | default: | |
730f927d KW |
359 | Perl_die(aTHX_ "panic: unhandled opcode %" IVdf |
360 | " for xs_builtin_func1_void()", (IV) ix); | |
6ac93b49 PE |
361 | } |
362 | ||
363 | XSRETURN(0); | |
364 | } | |
365 | ||
bd79e3f7 PE |
366 | XS(XS_builtin_created_as_string) |
367 | { | |
368 | dXSARGS; | |
369 | ||
370 | if(items != 1) | |
371 | croak_xs_usage(cv, "arg"); | |
372 | ||
373 | SV *arg = ST(0); | |
374 | SvGETMAGIC(arg); | |
375 | ||
376 | /* SV was created as string if it has POK and isn't bool */ | |
377 | ST(0) = boolSV(SvPOK(arg) && !SvIsBOOL(arg)); | |
378 | XSRETURN(1); | |
379 | } | |
380 | ||
381 | XS(XS_builtin_created_as_number) | |
382 | { | |
383 | dXSARGS; | |
384 | ||
385 | if(items != 1) | |
386 | croak_xs_usage(cv, "arg"); | |
387 | ||
388 | SV *arg = ST(0); | |
389 | SvGETMAGIC(arg); | |
390 | ||
391 | /* SV was created as number if it has NOK or IOK but not POK and is not bool */ | |
392 | ST(0) = boolSV(SvNIOK(arg) && !SvPOK(arg) && !SvIsBOOL(arg)); | |
393 | XSRETURN(1); | |
394 | } | |
395 | ||
852c1a84 PE |
396 | static OP *ck_builtin_func1(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) |
397 | { | |
398 | const struct BuiltinFuncDescriptor *builtin = NUM2PTR(const struct BuiltinFuncDescriptor *, SvUV(ckobj)); | |
399 | ||
edd58c23 | 400 | if(builtin->is_experimental) |
d0364e1b | 401 | warn_experimental_builtin(builtin->name); |
cc4aa213 | 402 | |
852c1a84 PE |
403 | SV *prototype = newSVpvs("$"); |
404 | SAVEFREESV(prototype); | |
405 | ||
406 | assert(entersubop->op_type == OP_ENTERSUB); | |
407 | ||
408 | entersubop = ck_entersub_args_proto(entersubop, namegv, prototype); | |
409 | ||
bd79e3f7 PE |
410 | OPCODE opcode = builtin->ckval; |
411 | if(!opcode) | |
412 | return entersubop; | |
413 | ||
852c1a84 PE |
414 | OP *parent = entersubop, *pushop, *argop; |
415 | ||
416 | pushop = cUNOPx(entersubop)->op_first; | |
417 | if (!OpHAS_SIBLING(pushop)) { | |
418 | pushop = cUNOPx(pushop)->op_first; | |
419 | } | |
420 | ||
421 | argop = OpSIBLING(pushop); | |
422 | ||
423 | if (!argop || !OpHAS_SIBLING(argop) || OpHAS_SIBLING(OpSIBLING(argop))) | |
424 | return entersubop; | |
425 | ||
426 | (void)op_sibling_splice(parent, pushop, 1, NULL); | |
427 | ||
1c57e396 | 428 | U8 wantflags = entersubop->op_flags & OPf_WANT; |
852c1a84 PE |
429 | |
430 | op_free(entersubop); | |
431 | ||
7c60f1f7 PE |
432 | if(opcode == OP_STRINGIFY) |
433 | /* Even though pp_stringify only looks at TOPs and conceptually works | |
434 | * on a single argument, it happens to be a LISTOP. I've no idea why | |
435 | */ | |
436 | return newLISTOPn(opcode, wantflags, | |
437 | argop, | |
438 | NULL); | |
439 | else | |
440 | return newUNOP(opcode, wantflags, argop); | |
6a2e756f PE |
441 | } |
442 | ||
10bccff2 PE |
443 | XS(XS_builtin_indexed) |
444 | { | |
445 | dXSARGS; | |
446 | ||
447 | switch(GIMME_V) { | |
448 | case G_VOID: | |
449 | Perl_ck_warner(aTHX_ packWARN(WARN_VOID), | |
450 | "Useless use of %s in void context", "builtin::indexed"); | |
451 | XSRETURN(0); | |
452 | ||
453 | case G_SCALAR: | |
454 | Perl_ck_warner(aTHX_ packWARN(WARN_SCALAR), | |
455 | "Useless use of %s in scalar context", "builtin::indexed"); | |
456 | ST(0) = sv_2mortal(newSViv(items * 2)); | |
457 | XSRETURN(1); | |
458 | ||
459 | case G_LIST: | |
460 | break; | |
461 | } | |
462 | ||
463 | SSize_t retcount = items * 2; | |
464 | EXTEND(SP, retcount); | |
465 | ||
466 | /* Copy from [items-1] down to [0] so we don't have to make | |
467 | * temporary copies */ | |
468 | for(SSize_t index = items - 1; index >= 0; index--) { | |
469 | /* Copy, not alias */ | |
470 | ST(index * 2 + 1) = sv_mortalcopy(ST(index)); | |
471 | ST(index * 2) = sv_2mortal(newSViv(index)); | |
472 | } | |
473 | ||
474 | XSRETURN(retcount); | |
475 | } | |
476 | ||
3c48288d MF |
477 | XS(XS_builtin_load_module); |
478 | XS(XS_builtin_load_module) | |
479 | { | |
480 | dXSARGS; | |
481 | if (items != 1) | |
482 | croak_xs_usage(cv, "arg"); | |
483 | SV *module_name = newSVsv(ST(0)); | |
484 | if (!SvPOK(module_name)) { | |
485 | SvREFCNT_dec(module_name); | |
486 | croak_xs_usage(cv, "defined string"); | |
487 | } | |
488 | load_module(PERL_LOADMOD_NOIMPORT, module_name, NULL, NULL); | |
489 | /* The loaded module's name is left intentionally on the stack for the | |
490 | * caller's benefit, and becomes load_module's return value. */ | |
491 | XSRETURN(1); | |
492 | } | |
493 | ||
10bccff2 PE |
494 | static OP *ck_builtin_funcN(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) |
495 | { | |
496 | const struct BuiltinFuncDescriptor *builtin = NUM2PTR(const struct BuiltinFuncDescriptor *, SvUV(ckobj)); | |
497 | ||
edd58c23 | 498 | if(builtin->is_experimental) |
d0364e1b | 499 | warn_experimental_builtin(builtin->name); |
10bccff2 PE |
500 | |
501 | SV *prototype = newSVpvs("@"); | |
502 | SAVEFREESV(prototype); | |
503 | ||
504 | assert(entersubop->op_type == OP_ENTERSUB); | |
505 | ||
506 | entersubop = ck_entersub_args_proto(entersubop, namegv, prototype); | |
507 | return entersubop; | |
508 | } | |
509 | ||
a64a1b91 DIM |
510 | static const char builtin_not_recognised[] = "'%" SVf "' is not recognised as a builtin function"; |
511 | ||
5a94e094 PE |
512 | #define NO_BUNDLE SHORTVER(255,255) |
513 | ||
852c1a84 PE |
514 | static const struct BuiltinFuncDescriptor builtins[] = { |
515 | /* constants */ | |
5a94e094 PE |
516 | { "true", SHORTVER(5,39), &XS_builtin_true, &ck_builtin_const, BUILTIN_CONST_TRUE, false }, |
517 | { "false", SHORTVER(5,39), &XS_builtin_false, &ck_builtin_const, BUILTIN_CONST_FALSE, false }, | |
852c1a84 PE |
518 | |
519 | /* unary functions */ | |
5a94e094 PE |
520 | { "is_bool", NO_BUNDLE, &XS_builtin_func1_scalar, &ck_builtin_func1, OP_IS_BOOL, true }, |
521 | { "weaken", SHORTVER(5,39), &XS_builtin_func1_void, &ck_builtin_func1, OP_WEAKEN, false }, | |
522 | { "unweaken", SHORTVER(5,39), &XS_builtin_func1_void, &ck_builtin_func1, OP_UNWEAKEN, false }, | |
523 | { "is_weak", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_IS_WEAK, false }, | |
524 | { "blessed", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_BLESSED, false }, | |
525 | { "refaddr", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_REFADDR, false }, | |
526 | { "reftype", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_REFTYPE, false }, | |
527 | { "ceil", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_CEIL, false }, | |
528 | { "floor", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_FLOOR, false }, | |
529 | { "is_tainted", SHORTVER(5,39), &XS_builtin_func1_scalar, &ck_builtin_func1, OP_IS_TAINTED, false }, | |
530 | { "trim", SHORTVER(5,39), &XS_builtin_trim, &ck_builtin_func1, 0, false }, | |
531 | { "stringify", NO_BUNDLE, &XS_builtin_func1_scalar, &ck_builtin_func1, OP_STRINGIFY, true }, | |
532 | ||
533 | { "created_as_string", NO_BUNDLE, &XS_builtin_created_as_string, &ck_builtin_func1, 0, true }, | |
534 | { "created_as_number", NO_BUNDLE, &XS_builtin_created_as_number, &ck_builtin_func1, 0, true }, | |
bd79e3f7 | 535 | |
3c48288d MF |
536 | { "load_module", NO_BUNDLE, &XS_builtin_load_module, &ck_builtin_func1, 0, true }, |
537 | ||
10bccff2 | 538 | /* list functions */ |
5a94e094 PE |
539 | { "indexed", SHORTVER(5,39), &XS_builtin_indexed, &ck_builtin_funcN, 0, false }, |
540 | { "export_lexically", NO_BUNDLE, &XS_builtin_export_lexically, NULL, 0, true }, | |
cb6b3bdb | 541 | |
5a94e094 | 542 | { NULL, 0, NULL, NULL, 0, false } |
852c1a84 PE |
543 | }; |
544 | ||
4f5a4bf5 | 545 | static bool S_parse_version(const char *vstr, const char *vend, UV *vmajor, UV *vminor) |
5a94e094 PE |
546 | { |
547 | /* Parse a string like "5.35" to yield 5 and 35. Ignores an optional | |
548 | * trailing third component e.g. "5.35.7". Returns false on parse errors. | |
549 | */ | |
550 | ||
4f5a4bf5 TC |
551 | const char *end = vend; |
552 | if (!grok_atoUV(vstr, vmajor, &end)) | |
553 | return FALSE; | |
554 | ||
555 | vstr = end; | |
556 | if (*vstr++ != '.') | |
557 | return FALSE; | |
5a94e094 | 558 | |
4f5a4bf5 TC |
559 | end = vend; |
560 | if (!grok_atoUV(vstr, vminor, &end)) | |
5a94e094 PE |
561 | return FALSE; |
562 | ||
563 | if(*vminor > 255) | |
564 | return FALSE; | |
565 | ||
4f5a4bf5 | 566 | vstr = end; |
5a94e094 PE |
567 | |
568 | if(vstr[0] == '.') { | |
569 | vstr++; | |
570 | ||
4f5a4bf5 TC |
571 | UV _dummy; |
572 | if(!grok_atoUV(vstr, &_dummy, &end)) | |
5a94e094 PE |
573 | return FALSE; |
574 | if(_dummy > 255) | |
575 | return FALSE; | |
576 | ||
4f5a4bf5 | 577 | vstr = end; |
5a94e094 PE |
578 | } |
579 | ||
c728cf57 | 580 | if(vstr != vend) |
5a94e094 PE |
581 | return FALSE; |
582 | ||
583 | return TRUE; | |
584 | } | |
585 | ||
586 | #define import_sym(sym) S_import_sym(aTHX_ sym) | |
587 | static void S_import_sym(pTHX_ SV *sym) | |
588 | { | |
589 | SV *ampname = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(sym))); | |
590 | SV *fqname = sv_2mortal(Perl_newSVpvf(aTHX_ "builtin::%" SVf, SVfARG(sym))); | |
591 | ||
592 | CV *cv = get_cv(SvPV_nolen(fqname), SvUTF8(fqname) ? SVf_UTF8 : 0); | |
593 | if(!cv) | |
594 | Perl_croak(aTHX_ builtin_not_recognised, sym); | |
595 | ||
596 | export_lexical(ampname, (SV *)cv); | |
597 | } | |
598 | ||
599 | #define import_builtin_bundle(ver) S_import_builtin_bundle(aTHX_ ver) | |
600 | static void S_import_builtin_bundle(pTHX_ U16 ver) | |
601 | { | |
602 | for(int i = 0; builtins[i].name; i++) { | |
603 | if(builtins[i].since_ver <= ver) | |
604 | import_sym(newSVpvn_flags(builtins[i].name, strlen(builtins[i].name), SVs_TEMP)); | |
605 | } | |
606 | } | |
607 | ||
6a2e756f PE |
608 | XS(XS_builtin_import); |
609 | XS(XS_builtin_import) | |
610 | { | |
611 | dXSARGS; | |
612 | ||
613 | if(!PL_compcv) | |
614 | Perl_croak(aTHX_ | |
2302ea7b | 615 | "builtin::import can only be called at compile time"); |
6a2e756f | 616 | |
bb0dc1a9 | 617 | prepare_export_lexical(); |
6a2e756f PE |
618 | |
619 | for(int i = 1; i < items; i++) { | |
620 | SV *sym = ST(i); | |
4f5a4bf5 TC |
621 | STRLEN symlen; |
622 | const char *sympv = SvPV(sym, symlen); | |
92ddeac0 | 623 | if(strEQ(sympv, "import") || strEQ(sympv, "unimport")) |
a64a1b91 | 624 | Perl_croak(aTHX_ builtin_not_recognised, sym); |
6a2e756f | 625 | |
5a94e094 | 626 | if(sympv[0] == ':') { |
4f5a4bf5 TC |
627 | UV vmajor, vminor; |
628 | if(!S_parse_version(sympv + 1, sympv + symlen, &vmajor, &vminor)) | |
c728cf57 | 629 | Perl_croak(aTHX_ "Invalid version bundle %" SVf_QUOTEDPREFIX, sym); |
6a2e756f | 630 | |
5a94e094 PE |
631 | U16 want_ver = SHORTVER(vmajor, vminor); |
632 | ||
633 | if(want_ver < SHORTVER(5,39) || | |
634 | /* round up devel version to next major release; e.g. 5.39 => 5.40 */ | |
635 | want_ver > SHORTVER(PERL_REVISION, PERL_VERSION + (PERL_VERSION % 2))) | |
636 | Perl_croak(aTHX_ "Builtin version bundle \"%s\" is not supported by Perl " PERL_VERSION_STRING, | |
637 | sympv); | |
638 | ||
639 | import_builtin_bundle(want_ver); | |
640 | ||
641 | continue; | |
642 | } | |
6a2e756f | 643 | |
5a94e094 | 644 | import_sym(sym); |
6a2e756f PE |
645 | } |
646 | ||
bb0dc1a9 | 647 | finish_export_lexical(); |
6a2e756f PE |
648 | } |
649 | ||
92ddeac0 PE |
650 | XS(XS_builtin_unimport); |
651 | XS(XS_builtin_unimport) | |
652 | { | |
653 | dXSARGS; | |
654 | ||
655 | if(!PL_compcv) | |
656 | Perl_croak(aTHX_ | |
657 | "builtin::unimport can only be called at compile time"); | |
658 | ||
659 | prepare_export_lexical(); | |
660 | ||
661 | for(int i = 1; i < items; i++) { | |
662 | SV *sym = ST(i); | |
663 | const char *sympv = SvPV_nolen(sym); | |
664 | if(strEQ(sympv, "import") || strEQ(sympv, "unimport")) | |
665 | Perl_croak(aTHX_ builtin_not_recognised, sym); | |
666 | ||
667 | SV *ampname = sv_2mortal(Perl_newSVpvf(aTHX_ "&%" SVf, SVfARG(sym))); | |
668 | SV *fqname = sv_2mortal(Perl_newSVpvf(aTHX_ "builtin::%" SVf, SVfARG(sym))); | |
669 | ||
670 | CV *cv = get_cv(SvPV_nolen(fqname), SvUTF8(fqname) ? SVf_UTF8 : 0); | |
671 | if(!cv) | |
672 | Perl_croak(aTHX_ builtin_not_recognised, sym); | |
673 | ||
674 | PADOFFSET off = pad_findmy_sv(ampname, 0); | |
675 | if((off == NOT_IN_PAD) || | |
676 | (PL_curpad[off] != (SV *)cv)) | |
677 | Perl_croak(aTHX_ | |
678 | "'%" SVf "' does not appear to be an imported builtin function", SVfARG(ampname)); | |
679 | ||
680 | /* Add a tombstone entry */ | |
681 | /* TODO: If the pad entry we found is going to go out of scope at the | |
682 | * same time as this tombstone would, we could not bother adding the | |
683 | * tombstone and instead COP_SEQ_MAX_HIGH_set() on the padname to | |
684 | * clear it. | |
685 | */ | |
686 | off = pad_add_name_sv(ampname, padadd_STATE|padadd_TOMBSTONE, 0, 0); | |
687 | SvREFCNT_dec(PL_curpad[off]); | |
688 | } | |
689 | ||
690 | COP_SEQMAX_INC; | |
691 | finish_export_lexical(); | |
692 | } | |
693 | ||
6a2e756f PE |
694 | void |
695 | Perl_boot_core_builtin(pTHX) | |
696 | { | |
852c1a84 PE |
697 | I32 i; |
698 | for(i = 0; builtins[i].name; i++) { | |
699 | const struct BuiltinFuncDescriptor *builtin = &builtins[i]; | |
700 | ||
701 | const char *proto = NULL; | |
702 | if(builtin->checker == &ck_builtin_const) | |
703 | proto = ""; | |
704 | else if(builtin->checker == &ck_builtin_func1) | |
705 | proto = "$"; | |
8be87d1d JR |
706 | else if(builtin->checker == &ck_builtin_funcN) |
707 | proto = "@"; | |
852c1a84 | 708 | |
d0364e1b PE |
709 | SV *name = newSVpvs_flags("builtin::", SVs_TEMP); |
710 | sv_catpv(name, builtin->name); | |
711 | CV *cv = newXS_flags(SvPV_nolen(name), builtin->xsub, __FILE__, proto, 0); | |
852c1a84 PE |
712 | XSANY.any_i32 = builtin->ckval; |
713 | ||
a5773895 DM |
714 | if ( builtin->xsub == &XS_builtin_func1_void |
715 | || builtin->xsub == &XS_builtin_func1_scalar) | |
716 | { | |
717 | /* these XS functions just call out to the relevant pp() | |
718 | * functions, so they must operate with a reference-counted | |
719 | * stack if the pp() do too. | |
720 | */ | |
721 | CvXS_RCSTACK_on(cv); | |
722 | } | |
723 | ||
852c1a84 PE |
724 | if(builtin->checker) { |
725 | cv_set_call_checker_flags(cv, builtin->checker, newSVuv(PTR2UV(builtin)), 0); | |
726 | } | |
727 | } | |
6a2e756f | 728 | |
92ddeac0 PE |
729 | newXS_flags("builtin::import", &XS_builtin_import, __FILE__, NULL, 0); |
730 | newXS_flags("builtin::unimport", &XS_builtin_unimport, __FILE__, NULL, 0); | |
6a2e756f PE |
731 | } |
732 | ||
733 | /* | |
734 | * ex: set ts=8 sts=4 sw=4 et: | |
735 | */ |