File Coverage

lib/meta.xs
Criterion Covered Total %
statement 354 430 82.3
branch 258 488 52.8
condition n/a
subroutine n/a
pod n/a
total 612 918 66.6


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, 2022 -- 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             #if !HAVE_PERL_VERSION(5,16,0)
16             # define true TRUE
17             # define false FALSE
18              
19             /* CvPROTO was just stored in SvPV */
20             # define CvPROTO(cv) SvPVX(cv)
21             # define CvPROTOLEN(cv) SvCUR(cv)
22             /* HvNAMELEN did not exist; stash names cannot contain \0 */
23             # define HvNAMELEN(stash) strlen(HvNAME(stash))
24             /* HvNAME and GvNAME could never be UTF-8 */
25             # define HvNAMEUTF8(hv) 0
26             # define GvNAMEUTF8(gv) 0
27              
28             # define gv_init_sv(gv, stash, sv, flags) \
29             gv_init(gv, stash, SvPV_nolen(sv), SvCUR(sv), SvUTF8(sv) | flags)
30             # define gv_init_pvn(gv, stash, pv, len, flags) \
31             gv_init(gv, stash, pv, len, flags)
32             #endif
33              
34             #if !HAVE_PERL_VERSION(5, 22, 0)
35             /* copypaste from perl-v5.22.0/perl.h */
36             # ifndef DEBUGGING
37             # if __has_builtin(__builtin_unreachable) \
38             || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5 || __GNUC__ > 4) /* 4.5 -> */
39             # define ASSUME(x) ((x) ? (void) 0 : __builtin_unreachable())
40             # elif defined(_MSC_VER)
41             # define ASSUME(x) __assume(x)
42             # elif defined(__ARMCC_VERSION) /* untested */
43             # define ASSUME(x) __promise(x)
44             # else
45             /* a random compiler might define assert to its own special optimization token
46             so pass it through to C lib as a last resort */
47             # define ASSUME(x) assert(x)
48             # endif
49             # else
50             # define ASSUME(x) assert(x)
51             # endif
52              
53             # define NOT_REACHED ASSUME(0)
54             #endif
55              
56             #if HAVE_PERL_VERSION(5, 26, 0)
57             # define HAVE_SUB_SIGNATURES
58             #endif
59              
60             #if HAVE_PERL_VERSION(5, 43, 3)
61             # define HAVE_OP_MULTIPARAM
62             #endif
63              
64             #if HAVE_PERL_VERSION(5, 43, 5)
65             # define HAVE_OP_MULTIPARAM_NAMED
66             #endif
67              
68             #ifndef av_count
69             # define av_count(av) (AvFILL(av)+1)
70             #endif
71              
72             #ifndef G_LIST
73             # define G_LIST G_ARRAY
74             #endif
75              
76             #ifndef CvREFCNT_inc
77             # define CvREFCNT_inc(cv) ((CV *)SvREFCNT_inc((SV *)(cv)))
78             #endif
79              
80             #if defined (DEBUGGING) && defined(PERL_USE_GCC_BRACE_GROUPS)
81             # define _MUST_SVTYPE_FROM_REFSV(rsv, type, svt) \
82             ({ type sv = (type)(SvUV(SvRV(rsv))); assert(sv && SvTYPE(sv) == svt); sv; })
83             #else
84             # define _MUST_SVTYPE_FROM_REFSV(rsv, type, svt) \
85             ((type)(SvUV(SvRV(rsv))))
86             #endif
87              
88             #define MUST_STASH_FROM_REFSV(sv) _MUST_SVTYPE_FROM_REFSV(sv, HV *, SVt_PVHV)
89             #define MUST_GV_FROM_REFSV(sv) _MUST_SVTYPE_FROM_REFSV(sv, GV *, SVt_PVGV)
90             #define MUST_CV_FROM_REFSV(sv) _MUST_SVTYPE_FROM_REFSV(sv, CV *, SVt_PVCV)
91              
92             #define SV_FROM_REFSV(sv) \
93             ((SV *)(SvUV(SvRV(sv))))
94              
95             #define wrap_sv_refsv(sv) S_wrap_sv_refsv(aTHX_ sv)
96 656           SV *S_wrap_sv_refsv(pTHX_ SV *sv)
97             {
98             const char *metaclass;
99 656           switch(SvTYPE(sv)) {
100             case SVt_PVGV: metaclass = "meta::glob"; break;
101 113           case SVt_PVCV: metaclass = "meta::subroutine"; break;
102 498           default: metaclass = "meta::variable"; break;
103             }
104 656           return sv_setref_uv(newSV(0), metaclass, PTR2UV(SvREFCNT_inc(sv)));
105             }
106              
107             #define wrap_stash(stash) S_wrap_stash(aTHX_ stash)
108 37           static SV *S_wrap_stash(pTHX_ HV *stash)
109             {
110             // TODO: Do we need to refcnt_inc stash?
111 37           return sv_setref_uv(newSV(0), "meta::package", PTR2UV(stash));
112             }
113              
114             struct CVwithOP {
115             CV *cv;
116             OP *op;
117             U32 flags;
118             };
119              
120             enum {
121             CVSIGNATURE_IS_METHOD = (1<<0),
122             };
123              
124             #define wrap_cv_signature(cv, op, flags) S_wrap_cv_signature(aTHX_ cv, op, flags)
125 5 50         static SV *S_wrap_cv_signature(pTHX_ CV *cv, OP *op, U32 flags)
126             {
127 5           struct CVwithOP ret = { .cv = CvREFCNT_inc(cv), .op = op, .flags = flags };
128 5           return sv_setref_pvn(newSV(0), "meta::subsignature", (const char *)&ret, sizeof(ret));
129             }
130              
131             #ifdef HAVE_OP_MULTIPARAM_NAMED
132              
133             # define newSVmultiparam_named(oaux) S_newSVmultiparam_named(aTHX_ oaux)
134             static SV *S_newSVmultiparam_named(pTHX_ struct op_multiparam_named_aux *oaux)
135             {
136             struct op_multiparam_named_aux ret = *oaux;
137             ret.namepv = savepvn(ret.namepv, ret.namelen);
138             return sv_setref_pvn(newSV(0), "meta::subsignature::named_param", (const char *)&ret, sizeof(ret));
139             }
140              
141             # define MULTIPARAM_NAMED_FROM_REFSV(refsv) \
142             (struct op_multiparam_named_aux *)SvPVX(SvRV(refsv))
143              
144             #endif /* HAVE_OP_MULTIPARAM_NAMED */
145              
146             #ifdef SVf_QUOTEDPREFIX
147             # define templateSVf_QUOTEDPREFIX "%" SVf_QUOTEDPREFIX
148             #else
149             # define templateSVf_QUOTEDPREFIX "\"%" SVf "\""
150             #endif
151              
152             #ifdef PVf_QUOTEDPREFIX
153             # define templatePVf_QUOTEDPREFIX "%" PVf_QUOTEDPREFIX
154             #else
155             # define templatePVf_QUOTEDPREFIX "\"%s\""
156             #endif
157              
158             #define gv_is_empty(gv) S_gv_is_empty(aTHX_ gv)
159 5           static bool S_gv_is_empty(pTHX_ GV *gv)
160             {
161 5 50         if(SvFAKE(gv) ||
162 5 100         GvSV(gv) ||
163 4 50         GvAV(gv) ||
164 4 50         GvHV(gv) ||
165 4 50         GvCV(gv) ||
166 4 50         GvIO(gv) ||
    50          
167 4 50         GvFORM(gv))
168 1           return false;
169              
170             /* TODO: any other safety checks? */
171             return true;
172             }
173              
174             /* Some helpers for warnings.pm
175             *
176             * The custom warning categories defined by warnings.pm are implemented
177             * entirely in the Perl code, so interacting with it means a lot of call_pv()
178             * wrapper functions.
179             *
180             * The warnings::warnif function is intended to be called from Perl, and
181             * presumes the caller stack will have a corresponding caller frame that it
182             * should skip. Since we're calling it here from XSUBs that does not happen,
183             * so we have to take extra measures to ensure it sees the correct caller
184             * context.
185             */
186              
187             #if HAVE_PERL_VERSION(5, 28, 0)
188             # define HAVE_WARNINGS_WARNIF_AT_LEVEL
189             #endif
190              
191             #define warnings_register_category(category) S_warnings_register_category(aTHX_ category)
192 13           static void S_warnings_register_category(pTHX_ const char *category)
193             {
194 13           dSP;
195 13           ENTER;
196              
197 13 50         EXTEND(SP, 1);
198 13 50         PUSHMARK(SP);
199 13           mPUSHp(category, strlen(category));
200 13           PUTBACK;
201              
202 13           call_pv("warnings::register_categories", G_VOID);
203              
204 13           LEAVE;
205 13           }
206              
207             #define warnings_warnsvif(category, msv) S_warnings_warnsvif(aTHX_ category, msv)
208 46           static void S_warnings_warnsvif(pTHX_ const char *category, SV *msv)
209             {
210 46           dSP;
211 46           ENTER;
212              
213             #ifdef HAVE_WARNINGS_WARNIF_AT_LEVEL
214 46 50         EXTEND(SP, 3);
215 46 50         PUSHMARK(SP);
216 46           mPUSHp(category, strlen(category));
217 46           mPUSHi(-1); // level = -1 because our XSUB does not have a caller frame
218 46           PUSHs(msv);
219 46           PUTBACK;
220              
221 46           call_pv("warnings::warnif_at_level", G_VOID);
222             #else
223             // warnings::warnif needs to see an extra call frame here. There's no way
224             // to hack this up using cx_pushblock etc... as that only works for pureperl
225             // CVs. We'll just have to use a trampoline
226             EXTEND(SP, 2);
227             PUSHMARK(SP);
228             mPUSHp(category, strlen(category));
229             PUSHs(msv);
230             PUTBACK;
231              
232             call_pv("meta::warnif_trampoline", G_VOID);
233             #endif
234              
235 46           LEAVE;
236 46           }
237              
238             #define META_WARNING_CATEGORY "meta::experimental"
239              
240             #define warn_experimental(fname) S_warn_experimental(aTHX_ fname)
241 46           static void S_warn_experimental(pTHX_ const char *fname)
242             {
243 46           warnings_warnsvif(META_WARNING_CATEGORY,
244             sv_2mortal(newSVpvf("%s is experimental and may be changed or removed without notice", fname)));
245 46           }
246              
247             #define warn_sub_deprecated(cv) S_warn_sub_deprecated(aTHX_ cv)
248 0           static void S_warn_sub_deprecated(pTHX_ CV *cv)
249             {
250 0           GV *gv = CvGV(cv);
251              
252 0           Perl_ck_warner(aTHX_ packWARN(WARN_DEPRECATED),
253             "%s::%s() is deprecated and may be removed without notice",
254 0           GvNAME(GvSTASH(gv)), GvNAME(gv));
255 0           }
256              
257             #if HAVE_PERL_VERSION(5, 38, 0)
258             # define HAVE_FEATURE_CLASS
259             #endif
260              
261             // Flags for get-alike methods
262             enum {
263             GET_OR_UNDEF,
264             GET_OR_THROW,
265             GET_OR_ADD,
266             ADD_OR_THROW,
267              
268             GET_OR_UNDEF_WITH_WARNING,
269             };
270              
271 4           static SV *S_get_metaglob_slot(pTHX_ SV *metaglob, U8 svt, const char *slotname, U8 ix)
272             {
273 4           GV *gv = MUST_GV_FROM_REFSV(metaglob);
274             SV *ret;
275 4           switch(svt) {
276 1           case SVt_PVMG: ret = GvSV (gv); break;
277 1           case SVt_PVAV: ret = (SV *)GvAV (gv); break;
278 2           case SVt_PVHV: ret = (SV *)GvHV (gv); break;
279 0 0         case SVt_PVCV: ret = (SV *)GvCVu(gv); break;
280             }
281              
282 4 100         if(ret)
283 2           return wrap_sv_refsv(ret);
284              
285 2           switch(ix) {
286 1           case GET_OR_THROW:
287 1           croak("Glob does not have a %s slot", slotname);
288             case GET_OR_UNDEF_WITH_WARNING:
289             case GET_OR_UNDEF:
290             return &PL_sv_undef;
291              
292 0           default:
293 0           NOT_REACHED;
294             }
295             }
296              
297 3           static void split_fqname(const char *namepv, STRLEN namelen,
298             const char **pkgnamepvp, STRLEN *pkgnamelenp, const char **basenamepvp, STRLEN *basenamelenp)
299             {
300             STRLEN pkgnamelen = 0;
301             const char *pkgnamepv = NULL;
302             STRLEN basenamelen = namelen;
303             const char *basenamepv = namepv;
304              
305 3           const char *s = namepv + namelen - 2;
306 33 100         for(/**/; s > namepv; s--) {
307 31 100         if(s[0] != ':' || s[1] != ':')
    100          
308             continue;
309              
310             /* s now points at the final occurence of '::' in the name
311             * pkgname is namepv up to s, basename is s+2 up to its original end */
312             pkgnamepv = namepv;
313 1           pkgnamelen = s - namepv;
314 1           basenamepv = s + 2;
315 1           basenamelen = namelen - (basenamepv - namepv);
316 1           break;
317             }
318              
319 3 50         if(pkgnamepvp) *pkgnamepvp = pkgnamepv;
320 3 50         if(pkgnamelenp) *pkgnamelenp = pkgnamelen;
321 3 50         if(basenamepvp) *basenamepvp = basenamepv;
322 3 50         if(basenamelenp) *basenamelenp = basenamelen;
323 3           }
324              
325             MODULE = meta PACKAGE = meta
326              
327             SV *
328             get_package(SV *pkgname)
329             CODE:
330 4           warn_experimental("meta::get_package");
331 4           RETVAL = wrap_stash(gv_stashsv(pkgname, GV_ADD));
332             OUTPUT:
333             RETVAL
334              
335             SV *
336             get_this_package()
337             CODE:
338 1           warn_experimental("meta::get_this_package");
339 1           RETVAL = wrap_stash(CopSTASH(PL_curcop));
340             OUTPUT:
341             RETVAL
342              
343             SV *
344             for_reference(SV *ref)
345             CODE:
346 9           warn_experimental("meta::for_reference");
347 9 50         if(!SvROK(ref))
348 0           croak("meta::for_reference requires a reference value");
349             /* TODO: maybe there's some kinds of SV we'll forbid here? */
350 9           RETVAL = wrap_sv_refsv(SvRV(ref));
351             OUTPUT:
352             RETVAL
353              
354             MODULE = meta PACKAGE = meta::package
355              
356             SV *
357             get(SV *cls, SV *pkgname)
358             CODE:
359 26 50         if(SvROK(cls))
360 0           croak("meta::package->get(name) should not be invoked on an instance "
361             "(did you mean to call one of the ->get_... methods?)");
362 26           warn_experimental("meta::package->get");
363 26           RETVAL = wrap_stash(gv_stashsv(pkgname, GV_ADD));
364             OUTPUT:
365             RETVAL
366              
367             bool
368             is_class(SV *metapkg)
369             CODE:
370             {
371             #ifdef HAVE_FEATURE_CLASS
372 3           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
373 3 50         RETVAL = HvSTASH_IS_CLASS(stash);
    100          
374             #else
375             PERL_UNUSED_VAR(metapkg);
376             RETVAL = false;
377             #endif
378             }
379             OUTPUT:
380             RETVAL
381              
382             SV *
383             name(SV *metapkg)
384             CODE:
385             {
386 6           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
387 12 50         RETVAL = newSVpvn_flags(HvNAME(stash), HvNAMELEN(stash), HvNAMEUTF8(stash) ? SVf_UTF8 : 0);
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    50          
    0          
    50          
    50          
    50          
    50          
    0          
    50          
388             }
389             OUTPUT:
390             RETVAL
391              
392             SV *
393             get_glob(SV *metapkg, SV *name)
394             ALIAS:
395             can_glob = GET_OR_UNDEF_WITH_WARNING
396             get_glob = GET_OR_THROW
397             try_get_glob = GET_OR_UNDEF
398             CODE:
399             {
400 9 50         if(ix == GET_OR_UNDEF_WITH_WARNING)
401 0           warn_sub_deprecated(cv);
402 9           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
403 9           HE *he = hv_fetch_ent(stash, name, 0, 0);
404 9 100         if(he) {
405 3           GV *gv = (GV *)HeVAL(he);
406             assert(SvTYPE(gv) == SVt_PVGV);
407 3           RETVAL = wrap_sv_refsv((SV *)gv);
408             }
409 6           else switch(ix) {
410 1           case GET_OR_THROW:
411 1 50         croak("Package " templatePVf_QUOTEDPREFIX " does not contain a glob called " templateSVf_QUOTEDPREFIX,
    50          
    50          
    0          
    50          
412             HvNAME(stash), SVfARG(name));
413             case GET_OR_UNDEF_WITH_WARNING:
414             case GET_OR_UNDEF:
415             RETVAL = &PL_sv_undef;
416             break;
417              
418 0           default:
419 0           NOT_REACHED;
420             }
421             }
422             OUTPUT:
423             RETVAL
424              
425             SV *
426             get_symbol(SV *metapkg, SV *name, SV *value = NULL)
427             ALIAS:
428             can_symbol = GET_OR_UNDEF_WITH_WARNING
429             get_symbol = GET_OR_THROW
430             try_get_symbol = GET_OR_UNDEF
431             get_or_add_symbol = GET_OR_ADD
432             add_symbol = ADD_OR_THROW
433             CODE:
434             {
435 47 50         if(ix == GET_OR_UNDEF_WITH_WARNING)
436 0           warn_sub_deprecated(cv);
437              
438 47           bool create = ix >= GET_OR_ADD;
439              
440 47 100         if(create) {
441 17 100         if(value && !SvROK(value))
    50          
442 0           croak("Expected a reference for the new value to add_symbol");
443             }
444             else {
445 30 50         if(value)
446 0           croak("meta::glob->get_symbol args");
447             }
448              
449 47           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
450 47           char sigil = SvPV_nolen(name)[0];
451             SV *valuesv = NULL;
452              
453 47 100         if(value) {
454 10           valuesv = SvRV(value);
455 10           switch(sigil) {
456 0           case '*':
457 0           croak("TODO: Cannot currently cope with adding GLOBs via ->add_symbol");
458             break;
459 4           case '$':
460 4 50         if(SvTYPE(valuesv) > SVt_PVMG)
461 0           croak("Expected a SCALAR reference for the new value to add_symbol('$...')");
462             break;
463 2           case '@':
464 2 50         if(SvTYPE(valuesv) != SVt_PVAV)
465 0           croak("Expected a ARRAY reference for the new value to add_symbol('@...')");
466             break;
467 2           case '%':
468 2 50         if(SvTYPE(valuesv) != SVt_PVHV)
469 0           croak("Expected a HASH reference for the new value to add_symbol('%%...')");
470             break;
471 2           case '&':
472 2 50         if(SvTYPE(valuesv) != SVt_PVCV)
473 0           croak("Expected a CODE reference for the new value to add_symbol('&...')");
474             break;
475 0           default:
476 0           croak("Unrecognised name sigil for add_symbol");
477             }
478             }
479              
480 47           SV *basename = newSVpvn_flags(SvPV_nolen(name) + 1, SvCUR(name) - 1,
481             (SvUTF8(name) ? SVf_UTF8 : 0) | SVs_TEMP);
482             SV *ret = NULL;
483 77 100         HE *he = hv_fetch_ent(stash, basename, create ? GV_ADD : 0, 0);
484 47 100         if(!he)
485 5           goto gv_missing;
486 42           SV *sv = HeVAL(he);
487              
488 42 100         if(create && SvTYPE(sv) != SVt_PVGV) {
    100          
489 11           gv_init_sv((GV *)sv, stash, basename, 0);
490 11           GvMULTI_on(sv);
491             }
492              
493 42 50         if(SvTYPE(sv) == SVt_PVGV) {
494             GV *gv = (GV *)sv;
495 42           switch(sigil) {
496             case '*': ret = (SV *) gv; break;
497 11           case '$': ret = GvSV (gv); break;
498 7           case '@': ret = (SV *)GvAV (gv); break;
499 8           case '%': ret = (SV *)GvHV (gv); break;
500 16 100         case '&': ret = (SV *)GvCVu(gv); break;
501             }
502             }
503 0 0         else if(SvROK(sv)) {
504             // GV-less optimisation; this is an RV to one kind of element
505 0           SV *rv = SvRV(sv);
506 0           switch(sigil) {
507             case '*': /* We know it isn't an SVt_PVGV */ ret = NULL; break;
508 0 0         case '$': ret = (SvTYPE(rv) <= SVt_PVMG) ? rv : NULL; break;
509 0 0         case '@': ret = (SvTYPE(rv) == SVt_PVAV) ? rv : NULL; break;
510 0 0         case '%': ret = (SvTYPE(rv) == SVt_PVHV) ? rv : NULL; break;
511 0 0         case '&': ret = (SvTYPE(rv) == SVt_PVCV) ? rv : NULL; break;
512             }
513             }
514             else
515 0           croak("TODO: Not sure what to do with SvTYPE(sv)=%d\n", SvTYPE(sv));
516              
517 42 100         if(ix == ADD_OR_THROW && ret)
518 1 50         croak("Package " templatePVf_QUOTEDPREFIX " already contains a symbol named " templateSVf_QUOTEDPREFIX,
    50          
    50          
    0          
    50          
519             HvNAME(stash), SVfARG(name));
520              
521 41 100         if(!ret && create) {
522             GV *gv = (GV *)sv;
523             ret = valuesv;
524              
525 15           switch(sigil) {
526 0           case '*':
527 0           croak("Cannot create the glob slot itself");
528 5           case '$':
529 5 100         if(!ret)
530 2           ret = newSV(0);
531 5 50         GvSV(gv) = SvREFCNT_inc(ret);
532             break;
533 4           case '@':
534 4 100         if(!ret)
535 2           ret = (SV *)newAV();
536 4 50         GvAV(gv) = (AV *)SvREFCNT_inc(ret);
537             break;
538 4           case '%':
539 4 100         if(!ret)
540 2           ret = (SV *)newHV();
541 4 50         GvHV(gv) = (HV *)SvREFCNT_inc(ret);
542             break;
543 2           case '&':
544 2 50         if(!ret)
545 0           croak("Cannot create a subroutine by ->get_or_add_symbol");
546 2           GvCV_set(gv, (CV *)SvREFCNT_inc(ret));
547 2           GvCVGEN(gv) = 0;
548 2           break;
549             }
550             }
551              
552 26           gv_missing:
553 28 100         if(ret)
554 37 100         RETVAL = (GIMME_V != G_VOID) ? wrap_sv_refsv(ret) : &PL_sv_undef;
555 9           else switch(ix) {
556 2           case GET_OR_THROW:
557 2 50         croak("Package " templatePVf_QUOTEDPREFIX " has no symbol named " templateSVf_QUOTEDPREFIX,
    50          
    50          
    0          
    50          
558             HvNAME(stash), SVfARG(name));
559             case GET_OR_UNDEF_WITH_WARNING:
560             case GET_OR_UNDEF:
561             RETVAL = &PL_sv_undef;
562             break;
563              
564 0           default:
565 0           NOT_REACHED;
566             }
567             }
568             OUTPUT:
569             RETVAL
570              
571             SV *
572             add_named_sub(SV *metapkg, SV *name, SV *value)
573             CODE:
574             {
575 1           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
576              
577 1 50         if(!SvROK(value) || SvTYPE(SvRV(value)) != SVt_PVCV)
    50          
578 0           croak("Expected a CODE reference for the new value to add_named_sub");
579             CV *cv = (CV *)SvRV(value);
580              
581 1           HE *he = hv_fetch_ent(stash, name, GV_ADD, 0);
582             GV *gv;
583             {
584             assert(he);
585 1           SV *sv = HeVAL(he);
586 1 50         if(SvTYPE(sv) != SVt_PVGV) {
587 1           gv_init_sv((GV *)sv, stash, name, 0);
588 1           GvMULTI_on(sv);
589             }
590              
591             gv = (GV *)sv;
592             }
593              
594 1 50         if(GvCVu(gv))
    50          
595 0 0         croak("Package " templatePVf_QUOTEDPREFIX " already contains symbol named &" templateSVf_QUOTEDPREFIX,
    0          
    0          
    0          
    0          
596             HvNAME(stash), SVfARG(name));
597              
598             /* Set these in the right order so the name GV works properly */
599 1           GvCV_set(gv, CvREFCNT_inc(cv));
600 1           GvCVGEN(gv) = 0;
601 1           CvGV_set(cv, gv);
602              
603 1           RETVAL = wrap_sv_refsv((SV *)cv);
604             }
605             OUTPUT:
606             RETVAL
607              
608             void
609             remove_symbol(SV *metapkg, SV *name)
610             CODE:
611             {
612 7           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
613 7           char sigil = SvPV_nolen(name)[0];
614 7           SV *basename = newSVpvn_flags(SvPV_nolen(name) + 1, SvCUR(name) - 1,
615             (SvUTF8(name) ? SVf_UTF8 : 0) | SVs_TEMP);
616 7           HE *he = hv_fetch_ent(stash, basename, 0, 0);
617 7 100         if(!he)
618 1           goto missing;
619 6           SV *sv = HeVAL(he);
620              
621 6 50         if(SvTYPE(sv) == SVt_PVGV) {
622             GV *gv = (GV *)sv;
623             SV *sv = NULL;
624 6           switch(sigil) {
625 0           case '*': croak("TODO: Cannot ->remove_symbol on a glob"); break;
626 1           case '$':
627 1           sv = GvSV(gv); GvSV(gv) = NULL;
628 1           break;
629 3           case '@':
630 3           sv = (SV *)GvAV(gv); GvAV(gv) = NULL;
631 3           break;
632 1           case '%':
633 1           sv = (SV *)GvHV(gv); GvHV(gv) = NULL;
634 1           break;
635 1           case '&':
636 1 50         sv = (SV *)GvCVu(gv); GvCV_set(gv, NULL);
637 1           GvCVGEN(gv) = 0;
638 1           break;
639             }
640              
641 6 100         if(!sv)
642 1           missing:
643 2 50         croak("Cannot remove non-existing symbol " templateSVf_QUOTEDPREFIX " from package " templatePVf_QUOTEDPREFIX,
    50          
    50          
    0          
    50          
644             SVfARG(name), HvNAME(stash));
645              
646 5           SvREFCNT_dec(sv);
647              
648             /* TODO: Perl core has a gv_try_downgrade() we could call here, but XS
649             * modules can't see it
650             */
651 5 100         if(gv_is_empty(gv))
652 4           hv_delete_ent(stash, basename, G_DISCARD, 0);
653             }
654 0 0         else if(SvROK(sv)) {
655             // GV-less optimisation; this is an RV to one kind of element
656 0           SV *rv = SvRV(sv);
657 0           switch(sigil) {
658 0           case '*': /* We know it isn't a SVt_PVGV */ goto missing; break;
659 0 0         case '$': if(SvTYPE(rv) > SVt_PVMG) goto missing; break;
660 0 0         case '@': if(SvTYPE(rv) != SVt_PVAV) goto missing; break;
661 0 0         case '%': if(SvTYPE(rv) != SVt_PVHV) goto missing; break;
662 0 0         case '&': if(SvTYPE(rv) != SVt_PVCV) goto missing; break;
663             }
664              
665 0           hv_delete_ent(stash, basename, G_DISCARD, 0);
666             }
667             else
668 0           croak("TODO: Not sure what to do with SvTYPE(sv)=%d\n", SvTYPE(sv));
669             }
670              
671             void
672             list_globs(SV *metapkg)
673             ALIAS:
674             list_all_globs = 0
675             list_globs = 1
676             list_subpackage_globs = 2
677             PPCODE:
678             {
679 3           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
680             UV retcount = 0;
681 3           hv_iterinit(stash);
682             HE *he;
683 60 100         while((he = hv_iternext(stash))) {
684 57           GV *gv = (GV *)HeVAL(he);
685             assert(SvTYPE(gv) == SVt_PVGV);
686 57 100         if(ix) {
687             STRLEN keylen;
688 38 50         const char *keypv = HePV(he, keylen);
689 38 50         bool is_subpackage = keylen > 2 && keypv[keylen-2] == ':' && keypv[keylen-1] == ':';
    100          
    50          
690 38 100         if(ix == 1 && is_subpackage)
691 19           continue;
692 32 100         if(ix == 2 && !is_subpackage)
693 13           continue;
694             }
695 38 50         EXTEND(SP, 1);
696 38           mPUSHs(wrap_sv_refsv((SV *)gv));
697 38           retcount++;
698             }
699 3           XSRETURN(retcount);
700             }
701              
702             void
703             _list_symbols(SV *metapkg, SV *sigils)
704             PPCODE:
705             {
706 2           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
707 2 50         const char *sigilfilter = sigils && SvOK(sigils) ? SvPV_nolen(sigils) : NULL;
    100          
708              
709             UV retcount = 0;
710 2           hv_iterinit(stash);
711             HE *he;
712 810 100         while((he = hv_iternext(stash))) {
713             STRLEN keylen;
714 808 50         const char *keypv = HePV(he, keylen);
715 808 100         bool is_subpackage = keylen > 2 && keypv[keylen-2] == ':' && keypv[keylen-1] == ':';
    100          
    50          
716 140           if(is_subpackage)
717 140           continue;
718             #define PUSH_SVREF_IF(sv, sigil) \
719             if((sv) && \
720             (!sigilfilter || strchr(sigilfilter, sigil))) { \
721             SV *_sv = (SV *)(sv); \
722             SV *namesv = newSVpvf("%c%.*s", sigil, (int)keylen, keypv); \
723             if(HeUTF8(he)) SvUTF8_on(namesv); \
724             EXTEND(SP, 2); \
725             mPUSHs(namesv); \
726             mPUSHs(wrap_sv_refsv(_sv)); \
727             retcount += 2; \
728             }
729              
730 668           SV *sv = HeVAL(he);
731 668 50         if(SvTYPE(sv) == SVt_PVGV) {
732             GV *gv = (GV *)sv;
733              
734 668 100         PUSH_SVREF_IF(GvSV (gv), '$');
    100          
    50          
    50          
    50          
    50          
735 668 100         PUSH_SVREF_IF(GvAV (gv), '@');
    100          
    50          
    50          
    50          
    50          
736 668 100         PUSH_SVREF_IF(GvHV (gv), '%');
    100          
    50          
    50          
    50          
    50          
737 668 50         PUSH_SVREF_IF(GvCVu(gv), '&');
    100          
    100          
    50          
    50          
    50          
    50          
738             }
739 0 0         else if(SvROK(sv)) {
740             // GV-less optimisation; this is an RV to one kind of element
741 0           SV *rv = SvRV(sv);
742 0           U8 type = SvTYPE(rv);
743              
744 0 0         PUSH_SVREF_IF(type <= SVt_PVMG ? rv : NULL, '$');
    0          
    0          
    0          
    0          
    0          
745 0 0         PUSH_SVREF_IF(type == SVt_PVAV ? rv : NULL, '@');
    0          
    0          
    0          
    0          
    0          
746 0 0         PUSH_SVREF_IF(type == SVt_PVHV ? rv : NULL, '%');
    0          
    0          
    0          
    0          
    0          
747 0 0         PUSH_SVREF_IF(type == SVt_PVCV ? rv : NULL, '&');
    0          
    0          
    0          
    0          
    0          
748             }
749             else
750 0           croak("TODO: Not sure what to do with SvTYPE(sv)=%d\n", SvTYPE(sv));
751             }
752             #undef PUSH_SVREF_IF
753 2           XSRETURN(retcount);
754             }
755              
756             void
757             list_subpackages(SV *metapkg)
758             PPCODE:
759             {
760 1           HV *stash = MUST_STASH_FROM_REFSV(metapkg);
761             UV retcount = 0;
762 1           hv_iterinit(stash);
763             HE *he;
764 20 100         while((he = hv_iternext(stash))) {
765             STRLEN keylen;
766 19 50         const char *keypv = HePV(he, keylen);
767 19 50         bool is_subpackage = keylen > 2 && keypv[keylen-2] == ':' && keypv[keylen-1] == ':';
    100          
    50          
768 19 100         if(!is_subpackage)
769 13           continue;
770              
771 6           GV *gv = (GV *)HeVAL(he);
772             assert(SvTYPE(gv) == SVt_PVGV);
773 6           HV *substash = GvHV(gv);
774              
775 6 50         EXTEND(SP, 2);
776              
777 6           mPUSHp(keypv, keylen - 2);
778 6 50         if(HeUTF8(he))
    50          
779 0           SvUTF8_on(*SP);
780              
781 6           mPUSHs(wrap_stash(substash));
782              
783 6           retcount += 2;
784             }
785 1           XSRETURN(retcount);
786             }
787              
788             MODULE = meta PACKAGE = meta::symbol
789              
790             void
791             DESTROY(SV *metasym)
792             CODE:
793             {
794 656           SV *sv = SV_FROM_REFSV(metasym);
795 656           SvREFCNT_dec(sv);
796             }
797              
798             bool
799             is_scalar(SV *metasym)
800             CODE:
801             {
802 2           SV *sv = SV_FROM_REFSV(metasym);
803 2 50         RETVAL = SvTYPE(sv) <= SVt_PVMG;
804             }
805             OUTPUT:
806             RETVAL
807              
808             bool
809             _is_type(SV *metasym)
810             ALIAS:
811             is_glob = SVt_PVGV
812             is_array = SVt_PVAV
813             is_hash = SVt_PVHV
814             is_subroutine = SVt_PVCV
815             CODE:
816             {
817 12           SV *sv = SV_FROM_REFSV(metasym);
818 12 100         RETVAL = SvTYPE(sv) == ix;
819             }
820             OUTPUT:
821             RETVAL
822              
823             SV *
824             reference(SV *metasym)
825             CODE:
826             {
827 25           SV *sv = SV_FROM_REFSV(metasym);
828 25           RETVAL = newRV_inc(sv);
829             }
830             OUTPUT:
831             RETVAL
832              
833             MODULE = meta PACKAGE = meta::glob
834              
835             SV *
836             get(SV *cls, SV *globname)
837             ALIAS:
838             get = GET_OR_THROW
839             try_get = GET_OR_UNDEF
840             get_or_add = GET_OR_ADD
841             CODE:
842             {
843 6 50         if(SvROK(cls))
844 0           croak("meta::glob->get(name) should not be invoked on an instance "
845             "(did you mean to call one of the ->get_... methods?)");
846             bool create = (ix == GET_OR_ADD);
847              
848 6           warn_experimental("meta::glob->get");
849 11 100         GV *gv = gv_fetchsv(globname, create ? GV_ADDMULTI : 0, SVt_PVGV);
850 6 100         if(gv) {
851             assert(SvTYPE(gv) == SVt_PVGV);
852 3           RETVAL = wrap_sv_refsv((SV *)gv);
853             }
854 3           else switch(ix) {
855 1           case GET_OR_THROW:
856 1           croak("Symbol table does not contain a glob called " templateSVf_QUOTEDPREFIX,
857             SVfARG(globname));
858             case GET_OR_UNDEF:
859             RETVAL = &PL_sv_undef;
860             break;
861              
862 0           default:
863 0           NOT_REACHED;
864             }
865             }
866             OUTPUT:
867             RETVAL
868              
869             SV *
870             basename(SV *metaglob)
871             CODE:
872             {
873 36           GV *gv = MUST_GV_FROM_REFSV(metaglob);
874 36           RETVAL = newSVpvn_flags(GvNAME(gv), GvNAMELEN(gv), GvNAMEUTF8(gv) ? SVf_UTF8 : 0);
875             }
876             OUTPUT:
877             RETVAL
878              
879             SV *get_scalar(SV *metaglob)
880             ALIAS:
881             can_scalar = GET_OR_UNDEF_WITH_WARNING
882             get_scalar = GET_OR_THROW
883             try_get_scalar = GET_OR_UNDEF
884             CODE:
885 1 50         if(ix == GET_OR_UNDEF_WITH_WARNING)
886 0           warn_sub_deprecated(cv);
887 1           RETVAL = S_get_metaglob_slot(aTHX_ metaglob, SVt_PVMG, "scalar", ix);
888             OUTPUT:
889             RETVAL
890              
891             SV *get_array(SV *metaglob)
892             ALIAS:
893             can_array = GET_OR_UNDEF_WITH_WARNING
894             get_array = GET_OR_THROW
895             try_get_array = GET_OR_UNDEF
896             CODE:
897 1 50         if(ix == GET_OR_UNDEF_WITH_WARNING)
898 0           warn_sub_deprecated(cv);
899 1           RETVAL = S_get_metaglob_slot(aTHX_ metaglob, SVt_PVAV, "array", ix);
900             OUTPUT:
901             RETVAL
902              
903             SV *get_hash(SV *metaglob)
904             ALIAS:
905             can_hash = GET_OR_UNDEF_WITH_WARNING
906             get_hash = GET_OR_THROW
907             try_get_hash = GET_OR_UNDEF
908             CODE:
909 2 50         if(ix == GET_OR_UNDEF_WITH_WARNING)
910 0           warn_sub_deprecated(cv);
911 2           RETVAL = S_get_metaglob_slot(aTHX_ metaglob, SVt_PVHV, "hash", ix);
912             OUTPUT:
913             RETVAL
914              
915             SV *get_code(SV *metaglob)
916             ALIAS:
917             can_code = GET_OR_UNDEF_WITH_WARNING
918             get_code = GET_OR_THROW
919             try_get_code = GET_OR_UNDEF
920             CODE:
921 0 0         if(ix == GET_OR_UNDEF_WITH_WARNING)
922 0           warn_sub_deprecated(cv);
923 0           RETVAL = S_get_metaglob_slot(aTHX_ metaglob, SVt_PVCV, "code", ix);
924             OUTPUT:
925             RETVAL
926              
927             MODULE = meta PACKAGE = meta::variable
928              
929             void
930             value(SV *metavar)
931             PPCODE:
932             {
933 5 50         if(GIMME_V == G_VOID)
934             // TODO: warn?
935 0           XSRETURN(0);
936              
937             /* TODO: all of the-below is super-fragile and probably doesn't work
938             * properly with tied scalars/arrays/hashes. Eugh.
939             */
940              
941 5           SV *sv = SV_FROM_REFSV(metavar);
942 5 100         if(SvTYPE(sv) <= SVt_PVMG) {
943 1           SV *ret = sv_mortalcopy(sv);
944 1 50         XPUSHs(ret);
945 1           XSRETURN(1);
946             }
947 4 100         else if(SvTYPE(sv) == SVt_PVAV) {
948             /* Array */
949             AV *av = (AV *)sv;
950 2           UV count = av_count(av);
951              
952 2 100         if(GIMME_V == G_SCALAR) {
953 1 50         mXPUSHu(count);
954 1           XSRETURN(1);
955             }
956 1 50         EXTEND(SP, count);
957             UV i;
958 4 100         for(i = 0; i < count; i++)
959 3           PUSHs(sv_mortalcopy(*av_fetch(av, i, 0)));
960 1           XSRETURN(count);
961             }
962 2 50         else if(SvTYPE(sv) == SVt_PVHV) {
963             /* Hash */
964             HV *hv = (HV *)sv;
965             UV count = 0;
966 2           U8 gimme = GIMME_V;
967              
968             HE *he;
969 2           hv_iterinit(hv);
970 6 100         while((he = hv_iternext(hv))) {
971 4 50         SV *key = HeSVKEY(he);
972 0 0         if(!key)
973 4           key = newSVpvn_flags(HeKEY(he), HeKLEN(he), HeKFLAGS(he) | SVs_TEMP);
974              
975 4 100         if(gimme == G_LIST) {
976 2 50         EXTEND(SP, 2);
977 2           PUSHs(key);
978 2           PUSHs(HeVAL(he));
979             }
980 4           count++;
981             }
982              
983 2 100         if(gimme == G_LIST)
984 1           XSRETURN(count * 2);
985              
986 1           mPUSHu(count);
987 1           XSRETURN(1);
988             }
989             else
990 0           croak("Argh unrecognised SvTYPE(sv)=%d", SvTYPE(sv));
991             }
992              
993             MODULE = meta PACKAGE = meta::subroutine
994              
995             bool
996             is_method(SV *metasub)
997             CODE:
998             {
999             #ifdef HAVE_FEATURE_CLASS
1000 3           CV *cv = MUST_CV_FROM_REFSV(metasub);
1001 3 100         RETVAL = CvIsMETHOD(cv);
1002             #else
1003             PERL_UNUSED_VAR(metasub);
1004             RETVAL = false;
1005             #endif
1006             }
1007             OUTPUT:
1008             RETVAL
1009              
1010             SV *
1011             subname(SV *metasub)
1012             CODE:
1013             {
1014 5           CV *cv = MUST_CV_FROM_REFSV(metasub);
1015              
1016 5           GV *gv = CvGV(cv);
1017 5 50         if(!gv)
1018             RETVAL = &PL_sv_undef;
1019             else
1020 5 50         RETVAL = newSVpvf("%s::%s", HvNAME(GvSTASH(gv)), GvNAME(gv));
    50          
    50          
    0          
    50          
1021             }
1022             OUTPUT:
1023             RETVAL
1024              
1025             SV *
1026             set_subname(SV *metasub, SV *name)
1027             CODE:
1028             {
1029 3           CV *cv = MUST_CV_FROM_REFSV(metasub);
1030              
1031             STRLEN namelen;
1032 3           const char *namepv = SvPV(name, namelen);
1033              
1034             const char *pkgnamepv, *basenamepv;
1035             STRLEN pkgnamelen, basenamelen;
1036 3           split_fqname(namepv, namelen,
1037             &pkgnamepv, &pkgnamelen, &basenamepv, &basenamelen);
1038              
1039             HV *stash;
1040 3 100         if(pkgnamelen)
1041 1           stash = gv_stashpvn(pkgnamepv, pkgnamelen, GV_ADD | SvUTF8(name));
1042             else
1043 2           stash = CopSTASH(PL_curcop);
1044              
1045             /* We can't just change the name in the sub's GV because that might be
1046             * shared and break all kinds of things. We'll have to make a new GV.
1047             */
1048 3           GV *newgv = (GV *)newSV(0);
1049 3           gv_init_pvn(newgv, stash, basenamepv, basenamelen, SvUTF8(name));
1050              
1051 3           CvANON_off(cv);
1052 3           CvGV_set(cv, newgv);
1053              
1054             /* CvGV_set claimed a reference to newgv; we can drop it now */
1055 3           SvREFCNT_dec(newgv);
1056              
1057             RETVAL = SvREFCNT_inc(metasub);
1058             }
1059             OUTPUT:
1060             RETVAL
1061              
1062             SV *
1063             prototype(SV *metasub)
1064             CODE:
1065             {
1066 3           CV *cv = MUST_CV_FROM_REFSV(metasub);
1067              
1068 3 50         if(!SvPOK(cv))
1069             RETVAL = &PL_sv_undef;
1070             else
1071 3 50         RETVAL = newSVpvn_flags(CvPROTO(cv), CvPROTOLEN(cv), SvUTF8(cv));
    50          
    50          
    50          
1072             }
1073             OUTPUT:
1074             RETVAL
1075              
1076             SV *
1077             set_prototype(SV *metasub, SV *proto)
1078             CODE:
1079             {
1080 2           CV *cv = MUST_CV_FROM_REFSV(metasub);
1081              
1082 2 50         if(SvOK(proto))
1083 2           sv_copypv((SV *)cv, proto);
1084             else
1085 0           SvPOK_off((SV *)cv);
1086              
1087             RETVAL = SvREFCNT_inc(metasub);
1088             }
1089             OUTPUT:
1090             RETVAL
1091              
1092             SV *
1093             signature(SV *metasub)
1094             CODE:
1095             {
1096 6           CV *cv = MUST_CV_FROM_REFSV(metasub);
1097              
1098             RETVAL = &PL_sv_undef;
1099             #ifdef HAVE_SUB_SIGNATURES
1100 6 50         if(CvISXSUB(cv))
1101 0           goto nosig;
1102              
1103 6           OP *oproot = CvROOT(cv);
1104 6 50         if(!oproot)
1105 0           goto nosig;
1106              
1107             /* The optree of a signatured sub should be an OP_LEAVESUB at toplevel.
1108             * Nested inside will be maybe one or two OP_NULL[OP_LINESEQ[...]]
1109             * subtrees, inside of which will be a COP, OP_ARGCHECK, ...
1110             * It is the OP_ARGCHECK we are looking for
1111             */
1112              
1113             assert(oproot->op_type == OP_LEAVESUB);
1114 6           OP *o = cUNOPx(oproot)->op_first;
1115              
1116             U32 flags = 0;
1117              
1118             /* Descend into OP_NULL / OP_LINESEQ trees while skipping past COPs
1119             */
1120 26 50         while(o) {
1121 26 100         if(o->op_type == OP_NULL)
1122 5           o = cUNOPo->op_first;
1123 21 100         else if(o->op_type == OP_LINESEQ)
1124 9 50         o = (o->op_flags & OPf_KIDS) ? cUNOPo->op_first : NULL;
1125 12 100         else if(o->op_type == OP_NEXTSTATE || o->op_type == OP_DBSTATE)
1126 6 100         o = OpSIBLING(o);
1127             # ifdef HAVE_FEATURE_CLASS
1128 6 100         else if(o->op_type == OP_METHSTART)
1129 1 50         o = OpSIBLING(o), flags |= CVSIGNATURE_IS_METHOD;
1130             # endif
1131             else
1132             break;
1133             }
1134              
1135 6 100         if(!o)
1136 1           goto nosig;
1137              
1138 5 50         if(o->op_type == OP_ARGCHECK
1139             #ifdef HAVE_OP_MULTIPARAM
1140             || o->op_type == OP_MULTIPARAM
1141             #endif
1142             )
1143 5           RETVAL = wrap_cv_signature(cv, o, flags);
1144              
1145 6           nosig:
1146             ;
1147             #endif
1148             }
1149             OUTPUT:
1150             RETVAL
1151              
1152             MODULE = meta PACKAGE = meta::subsignature
1153              
1154             void
1155             DESTROY(SV *metasig)
1156             CODE:
1157             {
1158 5           struct CVwithOP *cvop = (struct CVwithOP *)SvPVX(SvRV(metasig));
1159              
1160 5           SvREFCNT_dec(cvop->cv);
1161             // ->op is not refcounted
1162             }
1163              
1164             SV *
1165             mandatory_params(SV *metasig)
1166             ALIAS:
1167             mandatory_params = 0
1168             optional_params = 1
1169             slurpy = 2
1170             min_args = 3
1171             max_args = 4
1172             CODE:
1173             {
1174             int params, opt_params;
1175             size_t n_named;
1176             char slurpy;
1177             #ifdef HAVE_SUB_SIGNATURES
1178 15           struct CVwithOP *cvop = (struct CVwithOP *)SvPVX(SvRV(metasig));
1179             # if HAVE_PERL_VERSION(5, 31, 5)
1180             # ifdef HAVE_OP_MULTIPARAM
1181             struct op_multiparam_aux *aux = NULL;
1182             if(cvop->op->op_type == OP_MULTIPARAM) {
1183             aux = (struct op_multiparam_aux *)cUNOP_AUXx(cvop->op)->op_aux;
1184              
1185             params = aux->n_positional;
1186             opt_params = params - aux->min_args;
1187             # ifdef HAVE_OP_MULTIPARAM_NAMED
1188             n_named = aux->n_named;
1189             # else
1190             n_named = 0;
1191             # endif
1192             slurpy = aux->slurpy;
1193             }
1194             else
1195             # endif
1196             {
1197 15           struct op_argcheck_aux *aux = (struct op_argcheck_aux *)cUNOP_AUXx(cvop->op)->op_aux;
1198 15           params = aux->params + ((cvop->flags & CVSIGNATURE_IS_METHOD) ? 1 : 0);
1199 15           opt_params = aux->opt_params;
1200             n_named = 0;
1201 15           slurpy = aux->slurpy;
1202             }
1203             # else
1204             UNOP_AUX_item *aux = cUNOP_AUXx(cvop->op)->op_aux;
1205             params = aux[0].iv;
1206             opt_params = aux[1].iv;
1207             n_named = 0;
1208             slurpy = aux[2].iv;
1209             # endif
1210              
1211 15           switch(ix) {
1212 3           case 0: /* mandatory_params */
1213 3           RETVAL = newSViv(params - opt_params);
1214 3           break;
1215 2           case 1: /* optional_params */
1216 2           RETVAL = newSViv(opt_params);
1217 2           break;
1218 4           case 2: /* slurpy */
1219 4 100         RETVAL = slurpy ? newSVpvf("%c", slurpy) : &PL_sv_undef;
1220             break;
1221 3           case 3: /* min_args */
1222             # ifdef HAVE_OP_MULTIPARAM_NAMED
1223             if(n_named) {
1224             int min_args = 0;
1225             /* Each mandatory named parameter counts for 2 arguments
1226             */
1227             for(size_t namedix = 0; namedix < n_named; namedix++) {
1228             struct op_multiparam_named_aux *named = &(aux->named[namedix]);
1229             if(named->is_required)
1230             min_args += 2;
1231             }
1232             /* If any named parameters are required then all of the optional
1233             * positional ones must be passed, to allow for them */
1234             if(min_args) {
1235             RETVAL = newSViv(params + min_args);
1236             break;
1237             }
1238             }
1239             /* else fallthrough */
1240             # endif
1241 3           RETVAL = newSViv(params - opt_params);
1242 3           break;
1243 3           case 4: /* max_args */
1244 3 100         RETVAL = (slurpy || n_named) ? &PL_sv_undef : newSViv(params);
1245             break;
1246              
1247 0           default:
1248 0           NOT_REACHED;
1249             }
1250             #endif
1251             }
1252             OUTPUT:
1253             RETVAL
1254              
1255             void
1256             named_params(SV *metasig)
1257             PPCODE:
1258             {
1259             #ifdef HAVE_OP_MULTIPARAM_NAMED
1260             struct CVwithOP *cvop = (struct CVwithOP *)SvPVX(SvRV(metasig));
1261             if(cvop->op->op_type != OP_MULTIPARAM)
1262             XSRETURN(0);
1263              
1264             struct op_multiparam_aux *aux = (struct op_multiparam_aux *)cUNOP_AUXx(cvop->op)->op_aux;
1265             size_t n_named = aux->n_named;
1266             EXTEND(SP, 2 * n_named);
1267             for(size_t namedix = 0; namedix < n_named; namedix++) {
1268             struct op_multiparam_named_aux *named = &(aux->named[namedix]);
1269             PUSHs(newSVpvn_flags(named->namepv, named->namelen, SVf_UTF8|SVs_TEMP));
1270             mPUSHs(newSVmultiparam_named(named));
1271             }
1272             XSRETURN(2 * n_named);
1273             #else
1274 1           XSRETURN(0);
1275             #endif
1276             }
1277              
1278             MODULE = meta PACKAGE = meta::subsignature::named_param
1279              
1280             void DESTROY(SV *metaparam)
1281             CODE:
1282             {
1283             #ifdef HAVE_OP_MULTIPARAM_NAMED
1284             struct op_multiparam_named_aux *aux = MULTIPARAM_NAMED_FROM_REFSV(metaparam);
1285             #endif
1286             }
1287              
1288             SV *name(SV *metaparam)
1289             CODE:
1290             {
1291             #ifdef HAVE_OP_MULTIPARAM_NAMED
1292             struct op_multiparam_named_aux *aux = MULTIPARAM_NAMED_FROM_REFSV(metaparam);
1293             RETVAL = newSVpvn_flags(aux->namepv, aux->namelen, SVf_UTF8);
1294             #else
1295             RETVAL = &PL_sv_undef;
1296             #endif
1297             }
1298             OUTPUT:
1299             RETVAL
1300              
1301             bool is_required(SV *metaparam)
1302             CODE:
1303             {
1304             #ifdef HAVE_OP_MULTIPARAM_NAMED
1305             struct op_multiparam_named_aux *aux = MULTIPARAM_NAMED_FROM_REFSV(metaparam);
1306             RETVAL = aux->is_required;
1307             #else
1308             RETVAL = false;
1309             #endif
1310             }
1311             OUTPUT:
1312             RETVAL
1313              
1314             BOOT:
1315 13           warnings_register_category(META_WARNING_CATEGORY);