File Coverage

src/parse_subsignature_ex.c
Criterion Covered Total %
statement 442 509 86.8
branch 259 368 70.3
condition n/a
subroutine n/a
pod n/a
total 701 877 79.9


line stmt bran cond sub pod time code
1             /* vi: set ft=c : */
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6              
7             #define HAVE_PERL_VERSION(R, V, S) \
8             (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
9              
10             #include "XSParseSublike.h"
11              
12             /* Skip this entire file on perls older than OP_ARGCHECK */
13             #if HAVE_PERL_VERSION(5, 26, 0)
14              
15             #define PERL_EXT
16             /* We need to be able to see FEATURE_*_IS_ENABLED */
17             #include "feature.h"
18             /* Also need KEY_sigvar */
19             #include "keywords.h"
20              
21             #include "parse_subsignature_ex.h"
22              
23             #include "lexer-additions.c.inc"
24              
25             #include "croak_from_caller.c.inc"
26             #include "make_argcheck_aux.c.inc"
27             #include "newSV_with_free.c.inc"
28              
29             #ifdef XOPf_xop_dump
30             # define HAVE_XOP_DUMP
31             #endif
32              
33             #ifndef av_count
34             # define av_count(av) (1 + AvFILL(av))
35             #endif
36              
37             #define newSVpvx(ptr) S_newSVpvx(aTHX_ ptr)
38 41           static SV *S_newSVpvx(pTHX_ void *ptr)
39             {
40 41           SV *sv = newSV(0);
41 41           sv_upgrade(sv, SVt_PV);
42 41           SvPVX(sv) = ptr;
43 41           return sv;
44             }
45              
46             /*
47             * Need to grab some things that aren't quite core perl API
48             */
49              
50             /* yyerror() is a long function and hard to emulate or copy-paste for our
51             * purposes; we'll reïmplement a smaller version of it
52             */
53              
54             #define LEX_IGNORE_UTF8_HINTS 0x00000002
55              
56             #define PL_linestr (PL_parser->linestr)
57              
58             #ifdef USE_UTF8_SCRIPTS
59             # define UTF cBOOL(!IN_BYTES)
60             #else
61             # define UTF cBOOL((PL_linestr && DO_UTF8(PL_linestr)) || ( !(PL_parser->lex_flags & LEX_IGNORE_UTF8_HINTS) && (PL_hints & HINT_UTF8)))
62             #endif
63              
64             #define yyerror(s) S_yyerror(aTHX_ s)
65 0           void S_yyerror(pTHX_ const char *s)
66             {
67 0           SV *message = sv_2mortal(newSVpvs_flags("", 0));
68              
69 0           char *context = PL_parser->oldbufptr;
70 0           STRLEN contlen = PL_parser->bufptr - PL_parser->oldbufptr;
71              
72 0 0         sv_catpvf(message, "%s at %s line %" IVdf,
73             s, OutCopFILE(PL_curcop), (IV)CopLINE(PL_curcop));
74              
75 0 0         if(context)
76 0 0         sv_catpvf(message, ", near \"%" UTF8f "\"",
    0          
    0          
    0          
    0          
77             UTF8fARG(UTF, contlen, context));
78              
79 0           sv_catpvf(message, "\n");
80              
81 0           PL_parser->error_count++;
82 0           warn_sv(message);
83 0           }
84              
85             /* Stolen from op.c */
86             #ifndef OpTYPE_set
87             # define OpTYPE_set(op, type) \
88             STMT_START { \
89             op->op_type = (OPCODE)type; \
90             op->op_ppaddr = PL_ppaddr[type]; \
91             } STMT_END
92             #endif
93              
94             #define alloc_LOGOP(a,b,c) S_alloc_LOGOP(aTHX_ a,b,c)
95 15           static LOGOP *S_alloc_LOGOP(pTHX_ I32 type, OP *first, OP* other)
96             {
97             dVAR;
98             LOGOP *logop;
99             OP *kid = first;
100 15           NewOp(1101, logop, 1, LOGOP);
101 15           OpTYPE_set(logop, type);
102 15           logop->op_first = first;
103 15           logop->op_other = other;
104 15 50         if (first)
105 15           logop->op_flags = OPf_KIDS;
106 15 50         while (kid && OpHAS_SIBLING(kid))
    50          
107 0           kid = OpSIBLING(kid);
108 15 50         if (kid)
109 15           OpLASTSIB_set(kid, (OP*)logop);
110 15           return logop;
111             }
112              
113             /* copypaste from core's pp.c */
114             static SV *
115 6           S_find_runcv_name(pTHX)
116             {
117             CV *cv;
118             GV *gv;
119             SV *sv;
120              
121 6           cv = find_runcv(0);
122 6 50         if (!cv)
123             return &PL_sv_no;
124              
125 6           gv = CvGV(cv);
126 6 50         if (!gv)
127             return &PL_sv_no;
128              
129 6           sv = sv_newmortal();
130 6           gv_fullname4(sv, gv, NULL, TRUE);
131 6           return sv;
132             }
133              
134             /*****************************
135             * Named arguments extension *
136             *****************************
137              
138             Signature handling of named arguments proceeds initially as with regular perl,
139             with the addition of one big op that handles all the named arguments at once.
140              
141             The generated optree will have additional steps after the OP_ARGCHECK +
142             OP_ARGELEM ops of positional parameters. Any CV with named parameters will
143             have a single OP_CUSTOM/pp_argelems_named that stands in place of any
144             OP_ARGELEM that would have been used for a final slurpy element, if present.
145             This stores details of all the named arguments in an array in its ->op_aux,
146             and processes all of the named arguments and the slurpy element all at once.
147             Following this will be a small optree per optional named parameter, consisting
148             of an OP_CUSTOM/pp_namedargexists, OP_CUSTOM/pp_namedargassign and the
149             defaulting expression.
150              
151             Temporarily during processing we make use of the SvPADSTALE flag on every pad
152             variable used to store a named parameter, to remember that no value has yet
153             been assigned into it. This is how we can detect required but missing named
154             parameters once argument processing is finished, and how the optional
155             parameters can have default expressions assigned into them.
156              
157             */
158              
159             enum {
160             OPp_NAMEDARGDEFELEM_IF_UNDEF = 1,
161             OPp_NAMEDARGDEFELEM_IF_FALSE = 2,
162             };
163              
164             static XOP xop_namedargexists;
165 16           static OP *pp_namedargexists(pTHX)
166             {
167 16           dSP;
168 16           dTARGET;
169              
170             bool ok = false;
171 16           switch(PL_op->op_private & 3) {
172 12           case 0:
173 12 50         ok = TARG && !SvPADSTALE(TARG);
    100          
174             break;
175              
176 2           case OPp_NAMEDARGDEFELEM_IF_UNDEF:
177 2 50         ok = TARG && SvOK(TARG);
    100          
178             break;
179              
180 2           case OPp_NAMEDARGDEFELEM_IF_FALSE:
181 2 50         ok = TARG && SvTRUE(TARG);
    100          
182             break;
183             }
184              
185             if(!ok)
186 8           return cLOGOP->op_other;
187              
188 8           RETURN;
189             }
190              
191             #define check_refalias_arg(priv, sv) S_check_refalias_arg(aTHX_ priv, sv)
192 15           static bool S_check_refalias_arg(pTHX_ U8 priv, SV *sv)
193             {
194 15 50         if(!sv || !SvROK(sv))
    50          
195             return false;
196              
197 15           SV *rv = SvRV(sv);
198              
199 15           switch(priv & OPpARGELEM_MASK) {
200 1           case OPpARGELEM_SV:
201 1 50         if(SvTYPE(rv) > SVt_PVMG)
202 0           return false;
203             break;
204              
205 11           case OPpARGELEM_AV:
206 11 100         if(SvTYPE(rv) != SVt_PVAV)
207 1           return false;
208             break;
209              
210 3           case OPpARGELEM_HV:
211 3 100         if(SvTYPE(rv) != SVt_PVHV)
212 1           return false;
213             break;
214             }
215              
216             return true;
217             }
218              
219             struct ArgElemsNamedParam {
220             U32 flags;
221             PADOFFSET padix;
222             U32 namehash;
223             Size_t namelen;
224             const char *namepv;
225             };
226             enum {
227             /* These flags are also stored in op_private of some ops so they have to
228             * fit in U8
229             */
230             NAMEDPARAMf_REQUIRED = (1<<0),
231             NAMEDPARAMf_UTF8 = (1<<1),
232             NAMEDPARAMf_REFALIAS = (1<<2),
233              
234             NAMEDPARAMf_REFSCALAR = (1<<3),
235             NAMEDPARAMf_REFARRAY = (2<<3),
236             NAMEDPARAMf_REFHASH = (3<<3),
237             };
238              
239             #define do_namedarg_assign(flags, padix, name, val) S_do_namedarg_assign(aTHX_ flags, padix, name, val)
240 50           static void S_do_namedarg_assign(pTHX_ U8 flags, PADOFFSET padix, SV *name, SV *val)
241             {
242 50           SV **padentry = &PAD_SVl(padix);
243              
244             /* This has to do all the work normally done by pp_argelem */
245             assert(TAINTING_get || !TAINT_get);
246 50 50         if(UNLIKELY(TAINT_get) && !SvTAINTED(val))
    0          
    0          
247 0           TAINT_NOT;
248              
249 50 100         if(flags & NAMEDPARAMf_REFALIAS) {
250             const char *exp_reftype = NULL;
251             U8 priv = 0;
252 5           switch(flags & NAMEDPARAMf_REFHASH) {
253 1           case NAMEDPARAMf_REFSCALAR: priv = OPpARGELEM_SV; exp_reftype = "SCALAR"; break;
254 3           case NAMEDPARAMf_REFARRAY: priv = OPpARGELEM_AV; exp_reftype = "ARRAY"; break;
255 1           case NAMEDPARAMf_REFHASH: priv = OPpARGELEM_HV; exp_reftype = "HASH"; break;
256             }
257 5 50         if(!check_refalias_arg(priv, val)) {
258 0 0         if(!name)
259             // TODO: Look up the param name from the padix... somehow?
260 0           name = newSVpvs_flags("???", SVs_TEMP);
261 0           croak_from_caller("Expected named argument '%" SVf "' to %" SVf " to be a reference to %s",
262             SVfARG(name), SVfARG(S_find_runcv_name(aTHX)), exp_reftype);
263             }
264              
265 5           SvREFCNT_dec(*padentry);
266 10 50         *padentry = SvREFCNT_inc(SvRV(val));
267             }
268             else
269 45 50         SvSetMagicSV(*padentry, val);
    50          
270              
271 50           SvPADSTALE_off(*padentry);
272 50           }
273              
274             static XOP xop_namedargassign;
275 8           static OP *pp_namedargassign(pTHX)
276             {
277 8           dSP;
278 8           SV *val = POPs;
279              
280 8           do_namedarg_assign(PL_op->op_private, PL_op->op_targ, NULL, val);
281              
282 8           RETURN;
283             }
284              
285 15           static int cmp_argelemsnamedparam(const void *_a, const void *_b)
286             {
287             const struct ArgElemsNamedParam *a = _a, *b = _b;
288 15 100         if(a->namehash < b->namehash)
289             return -1;
290 12 50         if(a->namehash > b->namehash)
291 12           return 1;
292             return 0;
293             }
294              
295             struct ArgElemsNamedAux {
296             UV start_argix;
297             Size_t n_params;
298             struct ArgElemsNamedParam params[0];
299             };
300              
301             static XOP xop_argelems_named;
302 36           static OP *pp_argelems_named(pTHX)
303             {
304 36           struct ArgElemsNamedAux *aux = (struct ArgElemsNamedAux *)cUNOP_AUX->op_aux;
305 36           AV *defav = GvAV(PL_defgv);
306              
307             HV *slurpy_hv = NULL;
308             AV *slurpy_av = NULL;
309             bool slurpy_ignore = false;
310              
311 36 100         if(PL_op->op_targ) {
312             /* We have a slurpy of some kind */
313 7           save_clearsv(&PAD_SVl(PL_op->op_targ));
314             }
315              
316 36 100         if(PL_op->op_private & OPpARGELEM_HV) {
317 6 100         if(PL_op->op_targ) {
318 5           slurpy_hv = (HV *)PAD_SVl(PL_op->op_targ);
319             assert(SvTYPE(slurpy_hv) == SVt_PVHV);
320             assert(HvKEYS(slurpy_hv) == 0);
321             }
322             else {
323             slurpy_ignore = true;
324             }
325             }
326 30 100         else if(PL_op->op_private & OPpARGELEM_AV) {
327 3 100         if(PL_op->op_targ) {
328 2           slurpy_av = (AV *)PAD_SVl(PL_op->op_targ);
329             assert(SvTYPE(slurpy_av) == SVt_PVAV);
330             assert(av_count(slurpy_av) == 0);
331             }
332             else {
333             slurpy_ignore = true;
334             }
335             }
336              
337 36           UV argix = aux->start_argix;
338 36           UV argc = av_count(defav);
339              
340             U32 parami;
341 36           UV n_params = aux->n_params;
342              
343             /* Before we process the incoming args we need to prepare *all* the param
344             * variable pad slots.
345             */
346 88 100         for(parami = 0; parami < n_params; parami++) {
347             struct ArgElemsNamedParam *param = &aux->params[parami];
348              
349 52           SV **padentry = &PAD_SVl(param->padix);
350             assert(padentry);
351 52           save_clearsv(padentry);
352              
353             /* A slight abuse of the PADSTALE flag so we can detect which parameters
354             * not been assigned to afterwards
355             */
356 52           SvPADSTALE_on(*padentry);
357             }
358              
359             SV *unrecognised_keynames = NULL;
360             UV n_unrecognised = 0;
361              
362 86 100         while(argix < argc) {
363             /* TODO: do we need av_fetch or can we cheat around it? */
364 50           SV *name = *av_fetch(defav, argix, 0);
365 50           argix++;
366 50 100         SV *val = argix < argc ? *av_fetch(defav, argix, 0) : &PL_sv_undef;
367 50           argix++;
368              
369             STRLEN namelen;
370 50           const char *namepv = SvPV(name, namelen);
371              
372             U32 namehash;
373 50 50         PERL_HASH(namehash, namepv, namelen);
374              
375             /* In theory we would get better performance at runtime by binary
376             * searching for a good starting index. In practice only actually starts
377             * saving measurable time once we start to get to literally hundreds of
378             * named parameters. This simple linear search is actually very quick per
379             * rejected element.
380             * If your perl function wants to declare hundreds of different named
381             * parameters you probably want to rethink your strategy. ;)
382             */
383             struct ArgElemsNamedParam *param = NULL;
384 73 100         for(parami = 0; parami < n_params; parami++) {
385 68           struct ArgElemsNamedParam *p = &aux->params[parami];
386              
387             /* Since the params are stored in hash key order, if we are already
388             * past it then we know we are done
389             */
390 68 100         if(p->namehash > namehash)
391             break;
392 65 100         if(p->namehash != namehash)
393 23           continue;
394              
395             /* TODO: This will be wrong for UTF-8 comparisons */
396 42 50         if(namelen != p->namelen)
397 0           continue;
398 42 50         if(!strnEQ(namepv, p->namepv, namelen))
399 0           continue;
400              
401             param = p;
402             break;
403             }
404              
405 50 100         if(param) {
406 42           do_namedarg_assign(param->flags, param->padix, name, val);
407             }
408 8 100         else if(slurpy_hv) {
409 1           hv_store_ent(slurpy_hv, name, newSVsv(val), 0);
410             }
411 7 100         else if(slurpy_av) {
412 4           av_push(slurpy_av, newSVsv(name));
413 4 100         if(argix <= argc)
414 3           av_push(slurpy_av, newSVsv(val));
415             }
416 3 100         else if(!slurpy_ignore) {
417 1 50         if(!unrecognised_keynames) {
418 1           unrecognised_keynames = newSVpvn("", 0);
419 1           SAVEFREESV(unrecognised_keynames);
420             }
421              
422 1 50         if(SvCUR(unrecognised_keynames))
423 0           sv_catpvs(unrecognised_keynames, ", ");
424 1           sv_catpvf(unrecognised_keynames, "'%" SVf "'", SVfARG(name));
425 1           n_unrecognised++;
426             }
427             }
428              
429 36 100         if(n_unrecognised) {
430 2 50         croak_from_caller("Unrecognised %s %" SVf " for subroutine %" SVf,
431             n_unrecognised > 1 ? "arguments" : "argument",
432             SVfARG(unrecognised_keynames), SVfARG(S_find_runcv_name(aTHX)));
433             }
434              
435             SV *missing_keynames = NULL;
436             UV n_missing = 0;
437              
438 86 100         for(parami = 0; parami < n_params; parami++) {
439             struct ArgElemsNamedParam *param = &aux->params[parami];
440 51           SV *targ = PAD_SVl(param->padix);
441              
442 51 100         if(!SvPADSTALE(targ))
443 41           continue;
444 10 100         if(!(param->flags & NAMEDPARAMf_REQUIRED))
445 6           continue;
446              
447 4 100         if(!missing_keynames) {
448 3           missing_keynames = newSVpvn("", 0);
449 3           SAVEFREESV(missing_keynames);
450             }
451              
452 4 100         if(SvCUR(missing_keynames))
453 1           sv_catpvs(missing_keynames, ", ");
454 4           sv_catpvf(missing_keynames, "'%s'", param->namepv);
455 4           n_missing++;
456             }
457              
458 35 100         if(n_missing) {
459 5 100         croak_from_caller("Missing %s %" SVf " for subroutine %" SVf,
460             n_missing > 1 ? "arguments" : "argument",
461             SVfARG(missing_keynames), SVfARG(S_find_runcv_name(aTHX)));
462             }
463              
464 32           return NORMAL;
465             }
466              
467             #ifdef HAVE_XOP_DUMP
468 0           static void opdump_argelems_named(pTHX_ const OP *o, struct Perl_OpDumpContext *ctx)
469             {
470 0           struct ArgElemsNamedAux *aux = (struct ArgElemsNamedAux *)cUNOP_AUXo->op_aux;
471              
472 0           opdump_printf(ctx, "START_ARGIX = %" UVuf "\n", aux->start_argix);
473 0           opdump_printf(ctx, "PARAMS = (%" UVuf ")\n", aux->n_params);
474              
475             U32 parami;
476 0 0         for(parami = 0; parami < aux->n_params; parami++) {
477             struct ArgElemsNamedParam *param = &aux->params[parami];
478              
479 0           opdump_printf(ctx, " [%d] = {.name=\"%s\", .namehash=%u .padix=%u, .flags=(",
480             parami,
481             param->namepv,
482             param->namehash,
483             (unsigned int)param->padix);
484              
485             bool need_comma = false;
486 0 0         if(param->flags & NAMEDPARAMf_UTF8)
487 0           opdump_printf(ctx, "%sUTF8", need_comma?",":""), need_comma = true;
488 0 0         if(param->flags & NAMEDPARAMf_REQUIRED)
489 0 0         opdump_printf(ctx, "%sREQUIRED", need_comma?",":""), need_comma = true;
490 0 0         if(param->flags & NAMEDPARAMf_REFALIAS)
491 0 0         opdump_printf(ctx, "%sREFALIAS", need_comma?",":""), need_comma = true;
492              
493 0           opdump_printf(ctx, ")}\n");
494             }
495 0           }
496             #endif
497              
498             static XOP xop_refargelem;
499 10           static OP *pp_refargelem(pTHX)
500             {
501 10           dSP;
502 10           U8 priv = PL_op->op_private;
503 10           IV argix = PTR2IV(cUNOP_AUX->op_aux);
504              
505             SV *sv;
506 10 100         if(PL_op->op_flags & OPf_STACKED)
507 3           sv = POPs;
508             else {
509 7           SV **svp = av_fetch(GvAV(PL_defgv), argix, FALSE);
510 7 50         sv = svp ? *svp : NULL;
511             }
512              
513 10           PUTBACK;
514              
515 10 100         if(!check_refalias_arg(priv, sv)) {
516             const char *exp_reftype = NULL;
517 2           switch(priv & OPpARGELEM_MASK) {
518 0           case OPpARGELEM_SV: exp_reftype = "SCALAR"; break;
519 1           case OPpARGELEM_AV: exp_reftype = "ARRAY"; break;
520 1           case OPpARGELEM_HV: exp_reftype = "HASH"; break;
521             }
522 2           croak_from_caller("Expected argument %" IVdf " to %" SVf " to be a reference to %s",
523             argix + 1, SVfARG(S_find_runcv_name(aTHX)), exp_reftype);
524             }
525              
526             /* Perform refaliasing into the pad */
527 8           SV **padentry = &(PAD_SVl(PL_op->op_targ));
528 8           save_clearsv(padentry);
529 8           SvREFCNT_dec(*padentry);
530 8 50         *padentry = SvREFCNT_inc(SvRV(sv));
531              
532 8           return PL_op->op_next;
533             }
534              
535             /* Parameter attribute extensions */
536             typedef struct SignatureAttributeRegistration SignatureAttributeRegistration;
537              
538             struct SignatureAttributeRegistration {
539             SignatureAttributeRegistration *next;
540              
541             const char *name;
542             STRLEN permit_hintkeylen;
543              
544             const struct XPSSignatureAttributeFuncs *funcs;
545             void *funcdata;
546             };
547              
548             static SignatureAttributeRegistration *sigattrs = NULL;
549              
550             #define find_registered_attribute(name) S_find_registered_attribute(aTHX_ name)
551 8           static SignatureAttributeRegistration *S_find_registered_attribute(pTHX_ const char *name)
552             {
553 8           HV *hints = GvHV(PL_hintgv);
554              
555             SignatureAttributeRegistration *reg;
556 8 50         for(reg = sigattrs; reg; reg = reg->next) {
557 8 50         if(!strEQ(name, reg->name))
558 0           continue;
559              
560 8 50         if(reg->funcs->permit_hintkey &&
    50          
561 8 50         (!hints || !hv_fetch(hints, reg->funcs->permit_hintkey, reg->permit_hintkeylen, 0)))
562 0           continue;
563              
564 8           return reg;
565             }
566              
567 0           croak("Unrecognised signature parameter attribute :%s", name);
568             }
569              
570             struct PendingSignatureFunc {
571             const struct XPSSignatureAttributeFuncs *funcs;
572             void *funcdata;
573             void *attrdata;
574             };
575              
576             #define PENDING_FROM_SV(sv) ((struct PendingSignatureFunc *)SvPVX(sv))
577              
578 8           static void pending_free(pTHX_ SV *sv)
579             {
580 8           struct PendingSignatureFunc *p = PENDING_FROM_SV(sv);
581              
582 8 50         if(p->funcs->free)
583 0           (*p->funcs->free)(aTHX_ p->attrdata, p->funcdata);
584 8           }
585              
586             #define NEW_SV_PENDING() newSV_with_free(sizeof(struct PendingSignatureFunc), &pending_free)
587              
588             struct NamedParamDetails {
589             PADOFFSET padix;
590             U8 flags;
591             char sigil;
592             };
593             struct SignatureParsingContext {
594             OP *positional_elems; /* OP_LINESEQ of every positional element, in order */
595             OP *named_elem_defops; /* OP_LINESEQ of those named elements that have defaulting expressions */
596             HV *named_details; /* SV ptrs to NamedParamDetails of every named parameter */
597             OP *slurpy_elem;
598              
599             /* Counters that replace what PL_parser->sig_* used to be */
600             IV n_elems;
601             IV n_optelems;
602             char slurpy_sigil;
603             };
604              
605 44           static void free_parsing_ctx(pTHX_ void *_sigctx)
606             {
607             struct SignatureParsingContext *sigctx = _sigctx;
608             /* TODO the rest */
609 44 100         if(sigctx->named_details)
610 27           SvREFCNT_dec((SV *)sigctx->named_details);
611 44           }
612              
613             #define sigctx_add_param(sigctx, paramctx) S_sigctx_add_param(aTHX_ sigctx, paramctx)
614 79           static void S_sigctx_add_param(pTHX_ struct SignatureParsingContext *sigctx, struct XPSSignatureParamContext *paramctx)
615             {
616 79 100         if(paramctx->is_named) {
617             /* A named scalar */
618              
619 41 50         if(paramctx->namelen) {
620 41 100         if(!sigctx->named_details)
621 27           sigctx->named_details = newHV();
622              
623             struct NamedParamDetails *details;
624 41           Newx(details, 1, struct NamedParamDetails);
625 41           *details = (struct NamedParamDetails){
626 41           .padix = paramctx->padix,
627 41           .flags = (!paramctx->defop) ? NAMEDPARAMf_REQUIRED : 0,
628 41           .sigil = paramctx->sigil,
629             };
630              
631 41 100         if(paramctx->is_refalias) {
632 4           details->flags |= NAMEDPARAMf_REFALIAS;
633 4           switch(paramctx->sigil) {
634 1           case '$': details->flags |= NAMEDPARAMf_REFSCALAR; break;
635 2           case '@': details->flags |= NAMEDPARAMf_REFARRAY; break;
636 1           case '%': details->flags |= NAMEDPARAMf_REFHASH; break;
637             }
638             }
639              
640 41           hv_store(sigctx->named_details, paramctx->namepv, paramctx->namelen, newSVpvx(details), 0);
641             }
642              
643 41           sigctx->named_elem_defops = op_append_elem(OP_LINESEQ, sigctx->named_elem_defops,
644             paramctx->op);
645              
646             /* Introduce the named parameter variable so later expressions can see it.
647             * This is done implicitly by newSTATEOP() for positional ones, but we
648             * must do it manually here.
649             */
650 41           intro_my();
651             }
652 38 100         else if(paramctx->is_refalias) {
653             /* A positional reference alias. */
654              
655             /* Acts as a positional for argument consuming purposes */
656 5 50         if(paramctx->op)
657 5           sigctx->positional_elems = op_append_list(OP_LINESEQ, sigctx->positional_elems,
658             newSTATEOP(0, NULL, paramctx->op));
659              
660 5           sigctx->n_elems++;
661 5 100         if(paramctx->defop)
662 2           sigctx->n_optelems++;
663             }
664 33 100         else if(paramctx->sigil == '$') {
665             /* A positional scalar */
666              
667             /* This call to newSTATEOP() must come AFTER parsing the defaulting
668             * expression because it involves an implicit intro_my() and so we must
669             * not introduce the new parameter variable beforehand (RT155630)
670             */
671 27 100         if(paramctx->op)
672 24           sigctx->positional_elems = op_append_list(OP_LINESEQ, sigctx->positional_elems,
673             newSTATEOP(0, NULL, paramctx->op));
674              
675 27           sigctx->n_elems++;
676 27 100         if(paramctx->defop)
677 4           sigctx->n_optelems++;
678             }
679             else {
680             /* The final slurpy */
681             assert(paramctx->sigil == '@' || paramctx->sigil == '%');
682              
683 6 100         if(paramctx->varop)
684 4           sigctx->slurpy_elem = newSTATEOP(0, NULL, paramctx->varop);
685              
686 6           sigctx->slurpy_sigil = paramctx->sigil;
687             }
688 79           }
689              
690 4           void XPS_signature_add_param(pTHX_ struct XSParseSublikeContext *ctx, struct XPSSignatureParamDetails *details)
691             {
692             /* We know that ctx is really a struct XPSContextWithPointer */
693 4           struct SignatureParsingContext *sigctx = ((struct XPSContextWithPointer *)ctx)->sigctx;
694              
695             /* Was added in version 7 and so far remains unchanged */
696 4 50         if(details->ver < 7 || details->ver > XSPARSESUBLIKE_ABI_VERSION)
697 0           croak("ABI version mismatch in .ver of XPSSignatureParamDetails structure passed to xps_signature_add_param()");
698              
699 4           struct XPSSignatureParamContext paramctx = {
700             .is_named = false,
701             .is_refalias = false,
702 4           .sigil = details->sigil,
703 4           .padix = details->padix,
704             .varop = NULL, /* wil be set below */
705             .defop = NULL,
706             };
707              
708             char padname_sigil = PadnamePV(PadnamelistARRAY(PL_comppad_name)[details->padix])[0];
709             assert(padname_sigil == details->sigil);
710             PERL_UNUSED_VAR(padname_sigil);
711              
712 4           paramctx.varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (sigctx->n_elems)));
713 4           switch(details->sigil) {
714 3           case '$': paramctx.varop->op_private |= OPpARGELEM_SV; break;
715 1           case '@': paramctx.varop->op_private |= OPpARGELEM_AV; break;
716 0           case '%': paramctx.varop->op_private |= OPpARGELEM_HV; break;
717             }
718 4           paramctx.varop->op_targ = details->padix;
719              
720 4 100         if(details->sigil == '$')
721 3           paramctx.op = paramctx.varop;
722              
723 4           sigctx_add_param(sigctx, ¶mctx);
724 4           }
725              
726 2           IV XPS_signature_query(pTHX_ struct XSParseSublikeContext *ctx, int q)
727             {
728             /* We know that ctx is really a struct XPSContextWithPointer */
729 2           struct SignatureParsingContext *sigctx = ((struct XPSContextWithPointer *)ctx)->sigctx;
730              
731 2           switch(q) {
732 2           case 0: return sigctx->n_elems;
733 0           case 1: return sigctx->n_optelems;
734 0           case 2: return sigctx->slurpy_sigil;
735 0           default:
736 0           croak("ARGH unreachable");
737             }
738             }
739              
740             #define parse_sigelem(sigctx, flags) S_parse_sigelem(aTHX_ sigctx, flags)
741 76           static void S_parse_sigelem(pTHX_ struct SignatureParsingContext *sigctx, U32 flags)
742             {
743 76           bool permit_attributes = flags & PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES;
744              
745 76           yy_parser *parser = PL_parser;
746              
747 76           int c = lex_peek_unichar(0);
748             int private;
749 76           struct XPSSignatureParamContext paramctx = { 0 };
750              
751             AV *pending = NULL;
752              
753 76 100         if((flags & PARSE_SUBSIGNATURE_NAMED_PARAMS) && c == ':') {
    100          
754 41           lex_read_unichar(0);
755 41           lex_read_space(0);
756              
757 41           paramctx.is_named = true;
758 41           c = lex_peek_unichar(0);
759             }
760              
761 76 100         if((flags & PARSE_SUBSIGNATURE_REFALIAS) && c == '\\') {
    100          
762 9           Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__REFALIASING),
763             "refaliases are experimental");
764              
765 9           lex_read_unichar(0);
766 9           lex_read_space(0);
767              
768 9           paramctx.is_refalias = true;
769 9           c = lex_peek_unichar(0);
770             }
771              
772             /* Be slightly helpful to folks who write \:$foo */
773 76 100         if((flags & PARSE_SUBSIGNATURE_NAMED_PARAMS) && c == ':')
    50          
774 0           croak("Named refalias parameters should be written :\\VAR, not \\:VAR");
775              
776 76           paramctx.sigil = c;
777 76           switch(paramctx.sigil) {
778             case '$': private = OPpARGELEM_SV; break;
779 8           case '@': private = OPpARGELEM_AV; break;
780 5           case '%': private = OPpARGELEM_HV; break;
781 0           case ':':
782 0           croak("Named signature elements are not permitted");
783 0           default:
784 0           croak("Expected a signature element at <%s>\n", parser->bufptr);
785             }
786              
787 76           char *lexname = parser->bufptr;
788              
789             /* Consume sigil */
790 76           lex_read_unichar(0);
791              
792             STRLEN lexname_len = 0;
793              
794 76 50         if(isIDFIRST_uni(lex_peek_unichar(0))) {
    50          
    100          
    0          
795 70           lex_read_unichar(0);
796 126 50         while(isALNUM_uni(lex_peek_unichar(0)))
    50          
    100          
    0          
797 56           lex_read_unichar(0);
798              
799 70           ENTER;
800 70           SAVEI16(PL_parser->in_my);
801 70           PL_parser->in_my = KEY_sigvar;
802              
803 70           lexname_len = PL_parser->bufptr - lexname;
804 70           paramctx.padix = pad_add_name_pvn(lexname, lexname_len, 0, NULL, NULL);
805              
806 70 100         if(paramctx.is_named) {
807 41           paramctx.namepv = lexname + 1;
808 41           paramctx.namelen = lexname_len - 1;
809              
810             /* named params don't get an individual varop */
811             }
812             else {
813 29 100         if(paramctx.is_refalias) {
814 5           paramctx.varop = newUNOP_AUX(OP_CUSTOM, 0, NULL, INT2PTR(UNOP_AUX_item *, (sigctx->n_elems)));
815 5           paramctx.varop->op_ppaddr = &pp_refargelem;
816             }
817             else
818 24           paramctx.varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (sigctx->n_elems)));
819 29           paramctx.varop->op_private |= private;
820 29           paramctx.varop->op_targ = paramctx.padix;
821             }
822              
823 70           LEAVE;
824             }
825              
826 76           lex_read_space(0);
827              
828 76 100         if(lex_peek_unichar(0) == ':') {
829 8 50         if(!permit_attributes)
830 0           croak("Attributes on signature parameters are not permitted");
831              
832 8           lex_read_unichar(0);
833 8           lex_read_space(0);
834              
835 8           SV *attrname = sv_newmortal(), *attrval = sv_newmortal();
836              
837 16 100         while(lex_scan_attrval_into(attrname, attrval)) {
838 8           lex_read_space(0);
839              
840 8           SignatureAttributeRegistration *reg = find_registered_attribute(SvPV_nolen(attrname));
841              
842 8           void *attrdata = NULL;
843 8 50         if(reg->funcs->apply)
844 8           (*reg->funcs->apply)(aTHX_ ¶mctx, attrval, &attrdata, reg->funcdata);
845              
846 8 50         if(attrdata || reg->funcs->post_defop) {
    50          
847 8 50         if(!pending) {
848 8           pending = newAV();
849 8           SAVEFREESV(pending);
850             }
851              
852             SV *psv;
853 8           av_push(pending, psv = NEW_SV_PENDING());
854              
855 8           PENDING_FROM_SV(psv)->funcs = reg->funcs;
856 8           PENDING_FROM_SV(psv)->funcdata = reg->funcdata;
857 8           PENDING_FROM_SV(psv)->attrdata = attrdata;
858             }
859              
860 8 50         if(lex_peek_unichar(0) == ':') {
861 0           lex_read_unichar(0);
862 0           lex_read_space(0);
863             }
864             }
865             }
866              
867 76 100         if(paramctx.sigil == '$' || paramctx.is_refalias) {
    100          
868 71 100         if(paramctx.is_named) {
869             }
870             else {
871 30 50         if(sigctx->slurpy_sigil)
872 0           yyerror("Slurpy parameters not last");
873             }
874              
875             bool default_if_undef = false, default_if_false = false;
876 127           if(lex_consume("=") ||
877 110 100         (default_if_undef = lex_consume("//=")) ||
878 54           (default_if_false = lex_consume("||="))) {
879 19           OP *defexpr = parse_termexpr(PARSE_OPTIONAL);
880 19 50         if(PL_parser->error_count)
881 0           croak("Expected a defaulting expression for optional parameter");
882 19 100         if(!paramctx.is_named && !paramctx.varop) {
    100          
883             /* We permit `= undef` and the blank `=` but nothing else */
884 4 100         if(defexpr && defexpr->op_type != OP_UNDEF)
    100          
885 1           croak("Unnamed positional parameters cannot have defaulting expressions");
886             }
887              
888 18 100         if(paramctx.is_named) {
889 9           OP *assignop = newUNOP(OP_CUSTOM, 0, defexpr);
890 9           assignop->op_ppaddr = &pp_namedargassign;
891 9           assignop->op_targ = paramctx.padix;
892 9 100         if(paramctx.is_refalias) {
893 1           assignop->op_private |= NAMEDPARAMf_REFALIAS;
894 1           switch(paramctx.sigil) {
895 0           case '$': assignop->op_private |= NAMEDPARAMf_REFSCALAR; break;
896 1           case '@': assignop->op_private |= NAMEDPARAMf_REFARRAY; break;
897 0           case '%': assignop->op_private |= NAMEDPARAMf_REFHASH; break;
898             }
899             }
900              
901 9 50         OP *existsop = (OP *)alloc_LOGOP(OP_CUSTOM, assignop, LINKLIST(assignop));
902 9           existsop->op_ppaddr = &pp_namedargexists;
903 9           existsop->op_targ = paramctx.padix;
904 9 100         existsop->op_private =
    100          
905             default_if_undef ? OPp_NAMEDARGDEFELEM_IF_UNDEF :
906             default_if_false ? OPp_NAMEDARGDEFELEM_IF_FALSE :
907             0;
908              
909 9           OP *defop = newUNOP(OP_NULL, 0, existsop);
910              
911 9 50         LINKLIST(defop);
912              
913 9           defop->op_next = existsop; /* start of this fragment */
914 9           assignop->op_next = defop; /* after assign, stop this fragment */
915              
916 9           paramctx.op = defop;
917 9           paramctx.defop = defop;
918             }
919 9 100         else if(paramctx.varop) {
920             U8 private = 0;
921             #ifdef OPpARG_IF_UNDEF
922 6 100         if(default_if_undef) private |= OPpARG_IF_UNDEF;
923 6 100         if(default_if_false) private |= OPpARG_IF_FALSE;
924             #else
925             if(default_if_undef || default_if_false)
926             /* TODO: This would be possible with a custom op but we'd basically
927             * have to copy the behaviour of pp_argdefelem in that case
928             */
929             yyerror("This Perl version cannot handle if_undef/if_false defaulting expressions on positional parameters");
930             #endif
931              
932 6 100         if(!defexpr)
933 1           defexpr = newOP(OP_UNDEF, OPf_WANT_SCALAR);
934              
935 6 100         OP *defop = (OP *)alloc_LOGOP(OP_ARGDEFELEM, defexpr, LINKLIST(defexpr));
936 6           defop->op_targ = (PADOFFSET)sigctx->n_elems;
937 6           defop->op_private = private;
938              
939 6           paramctx.varop->op_flags |= OPf_STACKED;
940 6           op_sibling_splice(paramctx.varop, NULL, 0, defop);
941 6           defop = op_contextualize(defop, G_SCALAR);
942              
943 6 50         LINKLIST(paramctx.varop);
944              
945 6           paramctx.varop->op_next = defop;
946 6           defexpr->op_next = paramctx.varop;
947              
948 6           paramctx.op = paramctx.varop;
949 6           paramctx.defop = defop;
950             }
951             /* else this is `= undef` on anonymous param; nothing to do */
952             }
953             else {
954 52 50         if(sigctx->n_optelems)
955 0           yyerror("Mandatory parameter follows optional parameter");
956              
957 52 100         if(!paramctx.is_named)
958 20           paramctx.op = paramctx.varop;
959             }
960             }
961             else {
962 5 50         if(paramctx.is_named)
963 0           yyerror("Slurpy parameters may not be named");
964 5 50         if(sigctx->slurpy_sigil)
965 0           yyerror("Multiple slurpy parameters not allowed");
966              
967 5 50         if(lex_peek_unichar(0) == '=')
968 0           yyerror("A slurpy parameter may not have a default value");
969             }
970              
971 75 100         if(pending) {
972 16 50         for(int i = 0; i <= AvFILL(pending); i++) {
    100          
973 8           struct PendingSignatureFunc *p = PENDING_FROM_SV(AvARRAY(pending)[i]);
974              
975 8 50         if(p->funcs->post_defop)
976 8           (*p->funcs->post_defop)(aTHX_ ¶mctx, p->attrdata, p->funcdata);
977             }
978             }
979              
980             /* Only after we've run the post_defop hooks can we actually consume the
981             * result in paramctx.op
982             */
983 75           sigctx_add_param(sigctx, ¶mctx);
984 75           }
985              
986 44           OP *XPS_parse_subsignature_ex(pTHX_ int flags,
987             struct XPSContextWithPointer *ctx,
988             struct HooksAndData hooksanddata[],
989             size_t nhooks)
990             {
991             /* Mostly reconstructed logic from perl 5.28.0's toke.c and perly.y
992             */
993 44           yy_parser *parser = PL_parser;
994 44           struct SignatureParsingContext sigctx_ = { 0 };
995             struct SignatureParsingContext *const sigctx = &sigctx_;
996              
997 44 50         if(ctx)
998 44           ctx->sigctx = sigctx;
999              
1000             assert((flags & ~(PARSE_SUBSIGNATURE_NAMED_PARAMS|PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES|PARSE_SUBSIGNATURE_REFALIAS)) == 0);
1001              
1002 44           ENTER;
1003 44           SAVEDESTRUCTOR_X(&free_parsing_ctx, sigctx);
1004              
1005             IV hooki;
1006             const struct XSParseSublikeHooks *hooks;
1007             void *hookdata;
1008              
1009 94 100         FOREACH_HOOKS_FORWARD {
    100          
1010 50 50         if(hooks->ver >= 7 && hooks->start_signature)
    100          
1011 6           (*hooks->start_signature)(aTHX_ &(ctx->ctx), hookdata);
1012             }
1013              
1014 77 100         while(lex_peek_unichar(0) != ')') {
1015 76           lex_read_space(0);
1016 76           parse_sigelem(sigctx, flags);
1017              
1018 75 50         if(PL_parser->error_count) {
1019 0           LEAVE;
1020 0           return NULL;
1021             }
1022              
1023 75           lex_read_space(0);
1024 75           switch(lex_peek_unichar(0)) {
1025 42           case ')': goto endofelems;
1026             case ',': break;
1027 0           default:
1028 0           fprintf(stderr, "ARGH unsure how to proceed parse_subsignature at <%s>\n",
1029             parser->bufptr);
1030 0           croak("ARGH");
1031             break;
1032             }
1033              
1034 33           lex_read_unichar(0);
1035 33           lex_read_space(0);
1036             }
1037 1           endofelems:
1038              
1039 43 50         if (!FEATURE_SIGNATURES_IS_ENABLED)
    50          
    50          
    50          
1040 0           croak("Experimental subroutine signatures not enabled");
1041              
1042             #if !HAVE_PERL_VERSION(5, 37, 0)
1043             Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__SIGNATURES),
1044             "The signatures feature is experimental");
1045             #endif
1046              
1047 92 100         FOREACH_HOOKS_REVERSE {
    100          
1048 49 50         if(hooks->ver >= 7 && hooks->finish_signature)
    100          
1049 4           (*hooks->finish_signature)(aTHX_ &(ctx->ctx), hookdata);
1050             }
1051              
1052 43           char slurpy_sigil = sigctx->slurpy_sigil;
1053 43 100         if(!slurpy_sigil && sigctx->named_details)
    100          
1054             slurpy_sigil = '%';
1055              
1056 43           UNOP_AUX_item *aux = make_argcheck_aux(
1057             sigctx->n_elems,
1058             sigctx->n_optelems,
1059             slurpy_sigil);
1060              
1061 43           OP *checkop = newUNOP_AUX(OP_ARGCHECK, 0, NULL, aux);
1062              
1063 43           OP *ops = op_prepend_elem(OP_LINESEQ, newSTATEOP(0, NULL, NULL),
1064             op_prepend_elem(OP_LINESEQ, checkop, sigctx->positional_elems));
1065              
1066 43 100         if(sigctx->named_details) {
1067 27 50         UV n_params = HvKEYS(sigctx->named_details);
1068              
1069 27           struct ArgElemsNamedAux *aux = safemalloc(
1070 27           sizeof(struct ArgElemsNamedAux) + n_params * sizeof(struct ArgElemsNamedParam)
1071             );
1072              
1073 27           aux->start_argix = sigctx->n_elems;
1074 27           aux->n_params = n_params;
1075              
1076 27           struct ArgElemsNamedParam *param = &aux->params[0];
1077              
1078 27           hv_iterinit(sigctx->named_details);
1079             HE *iter;
1080 68 100         while((iter = hv_iternext(sigctx->named_details))) {
1081             STRLEN namelen;
1082 41 50         const char *namepv = HePV(iter, namelen);
1083 41           struct NamedParamDetails *details = (struct NamedParamDetails *)SvPVX(HeVAL(iter));
1084              
1085 82           *param = (struct ArgElemsNamedParam){
1086 82           .flags = details->flags |
1087 41 50         (HeUTF8(iter) ? NAMEDPARAMf_UTF8 : 0),
    50          
1088 41           .padix = details->padix,
1089 41           .namehash = HeHASH(iter),
1090 41           .namepv = savepvn(namepv, namelen),
1091             .namelen = namelen,
1092             };
1093 41           param++;
1094             }
1095              
1096 27 100         if(aux->n_params > 1) {
1097             /* Sort the params by hash value */
1098 13           qsort(&aux->params, aux->n_params, sizeof(aux->params[0]),
1099             &cmp_argelemsnamedparam);
1100             }
1101              
1102 27           OP *argelems_named_op = newUNOP_AUX(OP_CUSTOM, 0, NULL, (UNOP_AUX_item *)aux);
1103 27           argelems_named_op->op_ppaddr = &pp_argelems_named;
1104 27 100         if(sigctx->slurpy_sigil) {
1105 5 100         if(sigctx->slurpy_elem && sigctx->slurpy_elem->op_type == OP_LINESEQ) {
    50          
1106             /* A real named slurpy variable */
1107 3 50         OP *o = OpSIBLING(cLISTOPx(sigctx->slurpy_elem)->op_first);
1108             assert(o);
1109             assert(o->op_type == OP_ARGELEM);
1110              
1111             /* Steal the slurpy's targ and private flags */
1112 3           argelems_named_op->op_targ = o->op_targ;
1113 3           argelems_named_op->op_private |= o->op_private & OPpARGELEM_MASK;
1114             }
1115             else {
1116             /* The slurpy is unnamed. Don't steal its targ but still set the
1117             * private flags
1118             */
1119 2           argelems_named_op->op_targ = 0;
1120 2 100         argelems_named_op->op_private = (sigctx->slurpy_sigil == '%') ? OPpARGELEM_HV :
    50          
1121             (sigctx->slurpy_sigil == '@') ? OPpARGELEM_AV :
1122             0;
1123             }
1124              
1125 5 100         if(sigctx->slurpy_elem) {
1126 3           op_free(sigctx->slurpy_elem);
1127 3           sigctx->slurpy_elem = NULL;
1128             }
1129             }
1130              
1131 27           ops = op_append_list(OP_LINESEQ, ops,
1132             newSTATEOP(0, NULL, NULL));
1133 27           ops = op_append_list(OP_LINESEQ, ops,
1134             argelems_named_op);
1135              
1136 27 100         if(sigctx->named_elem_defops)
1137             /* TODO: append each elem individually */
1138 9           ops = op_append_list(OP_LINESEQ, ops,
1139             sigctx->named_elem_defops);
1140             }
1141 16 100         else if(sigctx->slurpy_elem) {
1142 1           ops = op_append_list(OP_LINESEQ, ops, sigctx->slurpy_elem);
1143             }
1144              
1145             /* a nextstate at the end handles context correctly for an empty
1146             * sub body */
1147 43           ops = op_append_elem(OP_LINESEQ, ops, newSTATEOP(0, NULL, NULL));
1148              
1149 43           LEAVE;
1150              
1151 43           return ops;
1152             }
1153              
1154 9           void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
1155             {
1156             SignatureAttributeRegistration *reg;
1157 9           Newx(reg, 1, struct SignatureAttributeRegistration);
1158              
1159 9           *reg = (struct SignatureAttributeRegistration){
1160             .name = name,
1161             .funcs = funcs,
1162             .funcdata = funcdata,
1163             };
1164              
1165 9 50         if(funcs->permit_hintkey)
1166 9           reg->permit_hintkeylen = strlen(funcs->permit_hintkey);
1167              
1168 9           reg->next = sigattrs;
1169 9           sigattrs = reg;
1170 9           }
1171              
1172 28           void XPS_boot_parse_subsignature_ex(pTHX)
1173             {
1174 28           XopENTRY_set(&xop_namedargexists, xop_name, "namedargexists");
1175 28           XopENTRY_set(&xop_namedargexists, xop_desc, "named argument element exists test");
1176 28           XopENTRY_set(&xop_namedargexists, xop_class, OA_LOGOP);
1177 28           Perl_custom_op_register(aTHX_ &pp_namedargexists, &xop_namedargexists);
1178              
1179 28           XopENTRY_set(&xop_namedargassign, xop_name, "namedargassign");
1180 28           XopENTRY_set(&xop_namedargassign, xop_desc, "named argument element assignment");
1181 28           XopENTRY_set(&xop_namedargassign, xop_class, OA_UNOP);
1182 28           Perl_custom_op_register(aTHX_ &pp_namedargassign, &xop_namedargassign);
1183              
1184 28           XopENTRY_set(&xop_argelems_named, xop_name, "argelems_named");
1185 28           XopENTRY_set(&xop_argelems_named, xop_desc, "named parameter elements");
1186 28           XopENTRY_set(&xop_argelems_named, xop_class, OA_UNOP_AUX);
1187             #ifdef HAVE_XOP_DUMP
1188 28           XopENTRY_set(&xop_argelems_named, xop_dump, &opdump_argelems_named);
1189             #endif
1190 28           Perl_custom_op_register(aTHX_ &pp_argelems_named, &xop_argelems_named);
1191              
1192 28           XopENTRY_set(&xop_refargelem, xop_name, "refargelem");
1193 28           XopENTRY_set(&xop_refargelem, xop_desc, "refalias argument element");
1194 28           XopENTRY_set(&xop_refargelem, xop_class, OA_UNOP_AUX);
1195 28           Perl_custom_op_register(aTHX_ &pp_refargelem, &xop_refargelem);
1196 28           }
1197              
1198             #else /* !HAVE_PERL_VERSION(5, 26, 0) */
1199              
1200             void XPS_signature_add_param(pTHX_ struct XSParseSublikeContext *ctx, struct XPSSignatureParamDetails *details)
1201             {
1202             }
1203              
1204             IV XPS_signature_query(pTHX_ struct XSParseSublikeContext *ctx, int q)
1205             {
1206             return 0;
1207             }
1208              
1209             void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata)
1210             {
1211             croak("Custom subroutine signature attributes are not supported on this verison of Perl");
1212             }
1213              
1214             void XPS_boot_parse_subsignature_ex(pTHX)
1215             {
1216             }
1217             #endif