pass current package to custom type reifier
[p5sagit/Function-Parameters.git] / Parameters.xs
1 /*
2 Copyright 2012 Lukas Mai.
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of either: the GNU General Public License as published
6 by the Free Software Foundation; or the Artistic License.
7
8 See http://dev.perl.org/licenses/ for more information.
9  */
10
11 #ifdef __GNUC__
12  #if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ >= 5
13   #define PRAGMA_GCC_(X) _Pragma(#X)
14   #define PRAGMA_GCC(X) PRAGMA_GCC_(GCC X)
15  #endif
16 #endif
17
18 #ifndef PRAGMA_GCC
19  #define PRAGMA_GCC(X)
20 #endif
21
22 #ifdef DEVEL
23  #define WARNINGS_RESET PRAGMA_GCC(diagnostic pop)
24  #define WARNINGS_ENABLEW(X) PRAGMA_GCC(diagnostic warning #X)
25  #define WARNINGS_ENABLE \
26         WARNINGS_ENABLEW(-Wall) \
27         WARNINGS_ENABLEW(-Wextra) \
28         WARNINGS_ENABLEW(-Wundef) \
29         /* WARNINGS_ENABLEW(-Wshadow) :-( */ \
30         WARNINGS_ENABLEW(-Wbad-function-cast) \
31         WARNINGS_ENABLEW(-Wcast-align) \
32         WARNINGS_ENABLEW(-Wwrite-strings) \
33         /* WARNINGS_ENABLEW(-Wnested-externs) wtf? */ \
34         WARNINGS_ENABLEW(-Wstrict-prototypes) \
35         WARNINGS_ENABLEW(-Wmissing-prototypes) \
36         WARNINGS_ENABLEW(-Winline) \
37         WARNINGS_ENABLEW(-Wdisabled-optimization)
38
39 #else
40  #define WARNINGS_RESET
41  #define WARNINGS_ENABLE
42 #endif
43
44
45 #define PERL_NO_GET_CONTEXT
46 #include "EXTERN.h"
47 #include "perl.h"
48 #include "XSUB.h"
49
50 #include <string.h>
51
52
53 WARNINGS_ENABLE
54
55
56 #define HAVE_PERL_VERSION(R, V, S) \
57         (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
58
59 #if HAVE_PERL_VERSION(5, 16, 0)
60  #define IF_HAVE_PERL_5_16(YES, NO) YES
61 #else
62  #define IF_HAVE_PERL_5_16(YES, NO) NO
63 #endif
64
65 #if 0
66  #if HAVE_PERL_VERSION(5, 17, 6)
67   #error "internal error: missing definition of KEY_my (your perl is too new)"
68  #elif HAVE_PERL_VERSION(5, 15, 8)
69   #define S_KEY_my 134
70  #elif HAVE_PERL_VERSION(5, 15, 6)
71   #define S_KEY_my 133
72  #elif HAVE_PERL_VERSION(5, 15, 5)
73   #define S_KEY_my 132
74  #elif HAVE_PERL_VERSION(5, 13, 0)
75   #define S_KEY_my 131
76  #else
77   #error "internal error: missing definition of KEY_my (your perl is too old)"
78  #endif
79 #endif
80
81
82 #define MY_PKG "Function::Parameters"
83
84 #define HINTK_KEYWORDS MY_PKG "/keywords"
85 #define HINTK_FLAGS_   MY_PKG "/flags:"
86 #define HINTK_SHIFT_   MY_PKG "/shift:"
87 #define HINTK_ATTRS_   MY_PKG "/attrs:"
88 #define HINTK_REIFY_   MY_PKG "/reify:"
89
90 #define DEFSTRUCT(T) typedef struct T T; struct T
91
92 #define UV_BITS (sizeof (UV) * CHAR_BIT)
93
94 enum {
95         FLAG_NAME_OK      = 0x01,
96         FLAG_ANON_OK      = 0x02,
97         FLAG_DEFAULT_ARGS = 0x04,
98         FLAG_CHECK_NARGS  = 0x08,
99         FLAG_INVOCANT     = 0x10,
100         FLAG_NAMED_PARAMS = 0x20,
101         FLAG_TYPES_OK     = 0x40,
102         FLAG_CHECK_TARGS  = 0x80
103 };
104
105 DEFSTRUCT(KWSpec) {
106         unsigned flags;
107         I32 reify_type;
108         SV *shift;
109         SV *attrs;
110 };
111
112 static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
113
114 DEFSTRUCT(Resource) {
115         Resource *next;
116         void *data;
117         void (*destroy)(pTHX_ void *);
118 };
119
120 typedef Resource *Sentinel[1];
121
122 static void sentinel_clear_void(pTHX_ void *p) {
123         Resource **pp = p;
124         while (*pp) {
125                 Resource *cur = *pp;
126                 if (cur->destroy) {
127                         cur->destroy(aTHX_ cur->data);
128                 }
129                 cur->data = (void *)"no";
130                 cur->destroy = NULL;
131                 *pp = cur->next;
132                 Safefree(cur);
133         }
134 }
135
136 static Resource *sentinel_register(Sentinel sen, void *data, void (*destroy)(pTHX_ void *)) {
137         Resource *cur;
138
139         Newx(cur, 1, Resource);
140         cur->data = data;
141         cur->destroy = destroy;
142         cur->next = *sen;
143         *sen = cur;
144
145         return cur;
146 }
147
148 static void sentinel_disarm(Resource *p) {
149         p->destroy = NULL;
150 }
151
152 static void my_sv_refcnt_dec_void(pTHX_ void *p) {
153         SV *sv = p;
154         SvREFCNT_dec(sv);
155 }
156
157 static SV *sentinel_mortalize(Sentinel sen, SV *sv) {
158         sentinel_register(sen, sv, my_sv_refcnt_dec_void);
159         return sv;
160 }
161
162
163 #if HAVE_PERL_VERSION(5, 17, 2)
164  #define MY_OP_SLABBED(O) ((O)->op_slabbed)
165 #else
166  #define MY_OP_SLABBED(O) 0
167 #endif
168
169 DEFSTRUCT(OpGuard) {
170         OP *op;
171         bool needs_freed;
172 };
173
174 static void op_guard_init(OpGuard *p) {
175         p->op = NULL;
176         p->needs_freed = FALSE;
177 }
178
179 static OpGuard op_guard_transfer(OpGuard *p) {
180         OpGuard r = *p;
181         op_guard_init(p);
182         return r;
183 }
184
185 static OP *op_guard_relinquish(OpGuard *p) {
186         OP *o = p->op;
187         op_guard_init(p);
188         return o;
189 }
190
191 static void op_guard_update(OpGuard *p, OP *o) {
192         p->op = o;
193         p->needs_freed = o && !MY_OP_SLABBED(o);
194 }
195
196 static void op_guard_clear(pTHX_ OpGuard *p) {
197         if (p->needs_freed) {
198                 op_free(p->op);
199         }
200 }
201
202 static void free_op_guard_void(pTHX_ void *vp) {
203         OpGuard *p = vp;
204         op_guard_clear(aTHX_ p);
205         Safefree(p);
206 }
207
208 static void free_op_void(pTHX_ void *vp) {
209         OP *p = vp;
210         op_free(p);
211 }
212
213 #define sv_eq_pvs(SV, S) my_sv_eq_pvn(aTHX_ SV, "" S "", sizeof (S) - 1)
214
215 static int my_sv_eq_pvn(pTHX_ SV *sv, const char *p, STRLEN n) {
216         STRLEN sv_len;
217         const char *sv_p = SvPV(sv, sv_len);
218         return memcmp(sv_p, p, n) == 0;
219 }
220
221
222 #include "padop_on_crack.c.inc"
223
224
225 enum {
226         MY_ATTR_LVALUE = 0x01,
227         MY_ATTR_METHOD = 0x02,
228         MY_ATTR_SPECIAL = 0x04
229 };
230
231 static void my_sv_cat_c(pTHX_ SV *sv, U32 c) {
232         char ds[UTF8_MAXBYTES + 1], *d;
233         d = (char *)uvchr_to_utf8((U8 *)ds, c);
234         if (d - ds > 1) {
235                 sv_utf8_upgrade(sv);
236         }
237         sv_catpvn(sv, ds, d - ds);
238 }
239
240
241 #define MY_UNI_IDFIRST(C) isIDFIRST_uni(C)
242 #define MY_UNI_IDCONT(C)  isALNUM_uni(C)
243
244 static SV *my_scan_word(pTHX_ Sentinel sen, bool allow_package) {
245         bool at_start, at_substart;
246         I32 c;
247         SV *sv = sentinel_mortalize(sen, newSVpvs(""));
248         if (lex_bufutf8()) {
249                 SvUTF8_on(sv);
250         }
251
252         at_start = at_substart = TRUE;
253         c = lex_peek_unichar(0);
254
255         while (c != -1) {
256                 if (at_substart ? MY_UNI_IDFIRST(c) : MY_UNI_IDCONT(c)) {
257                         lex_read_unichar(0);
258                         my_sv_cat_c(aTHX_ sv, c);
259                         at_substart = FALSE;
260                         c = lex_peek_unichar(0);
261                 } else if (allow_package && !at_substart && c == '\'') {
262                         lex_read_unichar(0);
263                         c = lex_peek_unichar(0);
264                         if (!MY_UNI_IDFIRST(c)) {
265                                 lex_stuff_pvs("'", 0);
266                                 break;
267                         }
268                         sv_catpvs(sv, "'");
269                         at_substart = TRUE;
270                 } else if (allow_package && (at_start || !at_substart) && c == ':') {
271                         lex_read_unichar(0);
272                         if (lex_peek_unichar(0) != ':') {
273                                 lex_stuff_pvs(":", 0);
274                                 break;
275                         }
276                         lex_read_unichar(0);
277                         c = lex_peek_unichar(0);
278                         if (!MY_UNI_IDFIRST(c)) {
279                                 lex_stuff_pvs("::", 0);
280                                 break;
281                         }
282                         sv_catpvs(sv, "::");
283                         at_substart = TRUE;
284                 } else {
285                         break;
286                 }
287                 at_start = FALSE;
288         }
289
290         return SvCUR(sv) ? sv : NULL;
291 }
292
293 static SV *my_scan_parens_tail(pTHX_ Sentinel sen, bool keep_backslash) {
294         I32 c, nesting;
295         SV *sv;
296         line_t start;
297
298         start = CopLINE(PL_curcop);
299
300         sv = sentinel_mortalize(sen, newSVpvs(""));
301         if (lex_bufutf8()) {
302                 SvUTF8_on(sv);
303         }
304
305         nesting = 0;
306         for (;;) {
307                 c = lex_read_unichar(0);
308                 if (c == EOF) {
309                         CopLINE_set(PL_curcop, start);
310                         return NULL;
311                 }
312
313                 if (c == '\\') {
314                         c = lex_read_unichar(0);
315                         if (c == EOF) {
316                                 CopLINE_set(PL_curcop, start);
317                                 return NULL;
318                         }
319                         if (keep_backslash || (c != '(' && c != ')')) {
320                                 sv_catpvs(sv, "\\");
321                         }
322                 } else if (c == '(') {
323                         nesting++;
324                 } else if (c == ')') {
325                         if (!nesting) {
326                                 break;
327                         }
328                         nesting--;
329                 }
330
331                 my_sv_cat_c(aTHX_ sv, c);
332         }
333
334         return sv;
335 }
336
337 static void my_check_prototype(pTHX_ Sentinel sen, const SV *declarator, SV *proto) {
338         char *start, *r, *w, *end;
339         STRLEN len;
340
341         /* strip spaces */
342         start = SvPV(proto, len);
343         end = start + len;
344
345         for (w = r = start; r < end; r++) {
346                 if (!isSPACE(*r)) {
347                         *w++ = *r;
348                 }
349         }
350         *w = '\0';
351         SvCUR_set(proto, w - start);
352         end = w;
353         len = end - start;
354
355         if (!ckWARN(WARN_ILLEGALPROTO)) {
356                 return;
357         }
358
359         /* check for bad characters */
360         if (strspn(start, "$@%*;[]&\\_+") != len) {
361                 SV *dsv = sentinel_mortalize(sen, newSVpvs(""));
362                 warner(
363                         packWARN(WARN_ILLEGALPROTO),
364                         "Illegal character in prototype for %"SVf" : %s",
365                         SVfARG(declarator),
366                         SvUTF8(proto)
367                                 ? sv_uni_display(
368                                         dsv,
369                                         proto,
370                                         len,
371                                         UNI_DISPLAY_ISPRINT
372                                 )
373                                 : pv_pretty(dsv, start, len, 60, NULL, NULL,
374                                         PERL_PV_ESCAPE_NONASCII
375                                 )
376                 );
377                 return;
378         }
379
380         for (r = start; r < end; r++) {
381                 switch (*r) {
382                         default:
383                                 warner(
384                                         packWARN(WARN_ILLEGALPROTO),
385                                         "Illegal character in prototype for %"SVf" : %s",
386                                         SVfARG(declarator), r
387                                 );
388                                 return;
389
390                         case '_':
391                                 if (r[1] && !strchr(";@%", *r)) {
392                                         warner(
393                                                 packWARN(WARN_ILLEGALPROTO),
394                                                 "Illegal character after '_' in prototype for %"SVf" : %s",
395                                                 SVfARG(declarator), r
396                                         );
397                                         return;
398                                 }
399                                 break;
400
401                         case '@':
402                         case '%':
403                                 if (r[1]) {
404                                         warner(
405                                                 packWARN(WARN_ILLEGALPROTO),
406                                                 "prototype after '%c' for %"SVf": %s",
407                                                 *r, SVfARG(declarator), r + 1
408                                         );
409                                         return;
410                                 }
411                                 break;
412
413                         case '\\':
414                                 r++;
415                                 if (strchr("$@%&*", *r)) {
416                                         break;
417                                 }
418                                 if (*r == '[') {
419                                         r++;
420                                         for (; r < end && *r != ']'; r++) {
421                                                 if (!strchr("$@%&*", *r)) {
422                                                         break;
423                                                 }
424                                         }
425                                         if (*r == ']' && r[-1] != '[') {
426                                                 break;
427                                         }
428                                 }
429                                 warner(
430                                         packWARN(WARN_ILLEGALPROTO),
431                                         "Illegal character after '\\' in prototype for %"SVf" : %s",
432                                         SVfARG(declarator), r
433                                 );
434                                 return;
435
436                         case '$':
437                         case '*':
438                         case '&':
439                         case ';':
440                         case '+':
441                                 break;
442                 }
443         }
444 }
445
446 static SV *parse_type(pTHX_ Sentinel, const SV *);
447
448 static SV *parse_type_paramd(pTHX_ Sentinel sen, const SV *declarator) {
449         I32 c;
450         SV *t;
451
452         t = my_scan_word(aTHX_ sen, TRUE);
453         lex_read_space(0);
454
455         c = lex_peek_unichar(0);
456         if (c == '[') {
457                 SV *u;
458
459                 lex_read_unichar(0);
460                 lex_read_space(0);
461                 my_sv_cat_c(aTHX_ t, c);
462
463                 u = parse_type(aTHX_ sen, declarator);
464                 sv_catsv(t, u);
465
466                 c = lex_peek_unichar(0);
467                 if (c != ']') {
468                         croak("In %"SVf": missing ']' after '%"SVf"'", SVfARG(declarator), SVfARG(t));
469                 }
470                 lex_read_unichar(0);
471                 lex_read_space(0);
472
473                 my_sv_cat_c(aTHX_ t, c);
474         }
475
476         return t;
477 }
478
479 static SV *parse_type(pTHX_ Sentinel sen, const SV *declarator) {
480         I32 c;
481         SV *t;
482
483         t = parse_type_paramd(aTHX_ sen, declarator);
484
485         c = lex_peek_unichar(0);
486         while (c == '|') {
487                 SV *u;
488
489                 lex_read_unichar(0);
490                 lex_read_space(0);
491
492                 my_sv_cat_c(aTHX_ t, c);
493                 u = parse_type_paramd(aTHX_ sen, declarator);
494                 sv_catsv(t, u);
495
496                 c = lex_peek_unichar(0);
497         }
498
499         return t;
500 }
501
502 static SV *reify_type(pTHX_ Sentinel sen, const SV *declarator, const KWSpec *spec, SV *name) {
503         AV *type_reifiers;
504         SV *t, *sv, **psv;
505         int n;
506         dSP;
507
508         type_reifiers = get_av(MY_PKG "::type_reifiers", 0);
509         assert(type_reifiers != NULL);
510
511         if (spec->reify_type < 0 || spec->reify_type > av_len(type_reifiers)) {
512                 croak("In %"SVf": internal error: reify_type [%ld] out of range [%ld]", SVfARG(declarator), (long)spec->reify_type, (long)(av_len(type_reifiers) + 1));
513         }
514
515         psv = av_fetch(type_reifiers, spec->reify_type, 0);
516         assert(psv != NULL);
517         sv = *psv;
518
519         ENTER;
520         SAVETMPS;
521
522         PUSHMARK(SP);
523         EXTEND(SP, 2);
524         PUSHs(name);
525         PUSHs(PL_curstname);
526         PUTBACK;
527
528         n = call_sv(sv, G_SCALAR);
529         SPAGAIN;
530
531         assert(n == 1);
532         /* don't warn about n being unused if assert() is compiled out */
533         n = n;
534
535         t = sentinel_mortalize(sen, SvREFCNT_inc(POPs));
536
537         PUTBACK;
538         FREETMPS;
539         LEAVE;
540
541         if (!SvTRUE(t)) {
542                 croak("In %"SVf": undefined type '%"SVf"'", SVfARG(declarator), SVfARG(name));
543         }
544
545         return t;
546 }
547
548
549 DEFSTRUCT(Param) {
550         SV *name;
551         PADOFFSET padoff;
552         SV *type;
553 };
554
555 DEFSTRUCT(ParamInit) {
556         Param param;
557         OpGuard init;
558 };
559
560 #define VEC(B) B ## _Vec
561
562 #define DEFVECTOR(B) DEFSTRUCT(VEC(B)) { \
563         B (*data); \
564         size_t used, size; \
565 }
566
567 DEFVECTOR(Param);
568 DEFVECTOR(ParamInit);
569
570 #define DEFVECTOR_INIT(N, B) static void N(VEC(B) *p) { \
571         p->used = 0; \
572         p->size = 23; \
573         Newx(p->data, p->size, B); \
574 } static void N(VEC(B) *)
575
576 DEFSTRUCT(ParamSpec) {
577         Param invocant;
578         VEC(Param) positional_required;
579         VEC(ParamInit) positional_optional;
580         VEC(Param) named_required;
581         VEC(ParamInit) named_optional;
582         Param slurpy;
583         PADOFFSET rest_hash;
584 };
585
586 DEFVECTOR_INIT(pv_init, Param);
587 DEFVECTOR_INIT(piv_init, ParamInit);
588
589 static void p_init(Param *p) {
590         p->name = NULL;
591         p->padoff = NOT_IN_PAD;
592         p->type = NULL;
593 }
594
595 static void ps_init(ParamSpec *ps) {
596         p_init(&ps->invocant);
597         pv_init(&ps->positional_required);
598         piv_init(&ps->positional_optional);
599         pv_init(&ps->named_required);
600         piv_init(&ps->named_optional);
601         p_init(&ps->slurpy);
602         ps->rest_hash = NOT_IN_PAD;
603 }
604
605 #define DEFVECTOR_EXTEND(N, B) static B (*N(VEC(B) *p)) { \
606         assert(p->used <= p->size); \
607         if (p->used == p->size) { \
608                 const size_t n = p->size / 2 * 3 + 1; \
609                 Renew(p->data, n, B); \
610                 p->size = n; \
611         } \
612         return &p->data[p->used]; \
613 } static B (*N(VEC(B) *))
614
615 DEFVECTOR_EXTEND(pv_extend, Param);
616 DEFVECTOR_EXTEND(piv_extend, ParamInit);
617
618 #define DEFVECTOR_CLEAR(N, B, F) static void N(pTHX_ VEC(B) *p) { \
619         while (p->used) { \
620                 p->used--; \
621                 F(aTHX_ &p->data[p->used]); \
622         } \
623         Safefree(p->data); \
624         p->data = NULL; \
625         p->size = 0; \
626 } static void N(pTHX_ VEC(B) *)
627
628 static void p_clear(pTHX_ Param *p) {
629         p->name = NULL;
630         p->padoff = NOT_IN_PAD;
631         p->type = NULL;
632 }
633
634 static void pi_clear(pTHX_ ParamInit *pi) {
635         p_clear(aTHX_ &pi->param);
636         op_guard_clear(aTHX_ &pi->init);
637 }
638
639 DEFVECTOR_CLEAR(pv_clear, Param, p_clear);
640 DEFVECTOR_CLEAR(piv_clear, ParamInit, pi_clear);
641
642 static void ps_clear(pTHX_ ParamSpec *ps) {
643         p_clear(aTHX_ &ps->invocant);
644
645         pv_clear(aTHX_ &ps->positional_required);
646         piv_clear(aTHX_ &ps->positional_optional);
647
648         pv_clear(aTHX_ &ps->named_required);
649         piv_clear(aTHX_ &ps->named_optional);
650
651         p_clear(aTHX_ &ps->slurpy);
652 }
653
654 static int ps_contains(pTHX_ const ParamSpec *ps, SV *sv) {
655         size_t i, lim;
656
657         if (ps->invocant.name && sv_eq(sv, ps->invocant.name)) {
658                 return 1;
659         }
660
661         for (i = 0, lim = ps->positional_required.used; i < lim; i++) {
662                 if (sv_eq(sv, ps->positional_required.data[i].name)) {
663                         return 1;
664                 }
665         }
666
667         for (i = 0, lim = ps->positional_optional.used; i < lim; i++) {
668                 if (sv_eq(sv, ps->positional_optional.data[i].param.name)) {
669                         return 1;
670                 }
671         }
672
673         for (i = 0, lim = ps->named_required.used; i < lim; i++) {
674                 if (sv_eq(sv, ps->named_required.data[i].name)) {
675                         return 1;
676                 }
677         }
678
679         for (i = 0, lim = ps->named_optional.used; i < lim; i++) {
680                 if (sv_eq(sv, ps->named_optional.data[i].param.name)) {
681                         return 1;
682                 }
683         }
684
685         return 0;
686 }
687
688 static void ps_free_void(pTHX_ void *p) {
689         ps_clear(aTHX_ p);
690         Safefree(p);
691 }
692
693 static int args_min(pTHX_ const ParamSpec *ps, const KWSpec *ks) {
694         int n = 0;
695         if (!ps) {
696                 return SvTRUE(ks->shift) ? 1 : 0;
697         }
698         if (ps->invocant.name) {
699                 n++;
700         }
701         n += ps->positional_required.used;
702         n += ps->named_required.used * 2;
703         return n;
704 }
705
706 static int args_max(const ParamSpec *ps) {
707         int n = 0;
708         if (!ps) {
709                 return -1;
710         }
711         if (ps->invocant.name) {
712                 n++;
713         }
714         n += ps->positional_required.used;
715         n += ps->positional_optional.used;
716         if (ps->named_required.used || ps->named_optional.used || ps->slurpy.name) {
717                 n = -1;
718         }
719         return n;
720 }
721
722 static size_t count_positional_params(const ParamSpec *ps) {
723         return ps->positional_required.used + ps->positional_optional.used;
724 }
725
726 static size_t count_named_params(const ParamSpec *ps) {
727         return ps->named_required.used + ps->named_optional.used;
728 }
729
730 static SV *my_eval(pTHX_ Sentinel sen, I32 floor, OP *op) {
731         SV *sv;
732         CV *cv;
733         dSP;
734
735         cv = newATTRSUB(floor, NULL, NULL, NULL, op);
736
737         ENTER;
738         SAVETMPS;
739
740         PUSHMARK(SP);
741         call_sv((SV *)cv, G_SCALAR | G_NOARGS);
742         SPAGAIN;
743         sv = sentinel_mortalize(sen, SvREFCNT_inc(POPs));
744
745         PUTBACK;
746         FREETMPS;
747         LEAVE;
748
749         return sv;
750 }
751
752 enum {
753         PARAM_INVOCANT = 0x01,
754         PARAM_NAMED    = 0x02
755 };
756
757 static PADOFFSET parse_param(
758         pTHX_
759         Sentinel sen,
760         const SV *declarator, const KWSpec *spec, ParamSpec *param_spec,
761         int *pflags, SV **pname, OpGuard *ginit, SV **ptype
762 ) {
763         I32 c;
764         char sigil;
765         SV *name;
766
767         assert(!ginit->op);
768         *pflags = 0;
769         *ptype = NULL;
770
771         c = lex_peek_unichar(0);
772
773         if (spec->flags & FLAG_TYPES_OK) {
774                 if (c == '(') {
775                         I32 floor;
776                         OP *expr;
777                         Resource *expr_sentinel;
778
779                         lex_read_unichar(0);
780
781                         floor = start_subparse(FALSE, 0);
782                         SAVEFREESV(PL_compcv);
783                         CvSPECIAL_on(PL_compcv);
784
785                         if (!(expr = parse_fullexpr(PARSE_OPTIONAL))) {
786                                 croak("In %"SVf": invalid type expression", SVfARG(declarator));
787                         }
788                         if (MY_OP_SLABBED(expr)) {
789                                 expr_sentinel = NULL;
790                         } else {
791                                 expr_sentinel = sentinel_register(sen, expr, free_op_void);
792                         }
793
794                         lex_read_space(0);
795                         c = lex_peek_unichar(0);
796                         if (c != ')') {
797                                 croak("In %"SVf": missing ')' after type expression", SVfARG(declarator));
798                         }
799                         lex_read_unichar(0);
800                         lex_read_space(0);
801
802                         SvREFCNT_inc_simple_void(PL_compcv);
803                         if (expr_sentinel) {
804                                 sentinel_disarm(expr_sentinel);
805                         }
806                         *ptype = my_eval(aTHX_ sen, floor, expr);
807                         if (!SvROK(*ptype)) {
808                                 *ptype = reify_type(aTHX_ sen, declarator, spec, *ptype);
809                         }
810                         if (!sv_isobject(*ptype)) {
811                                 croak("In %"SVf": (%"SVf") doesn't look like a type object", SVfARG(declarator), SVfARG(*ptype));
812                         }
813
814                         c = lex_peek_unichar(0);
815                 } else if (MY_UNI_IDFIRST(c)) {
816                         *ptype = parse_type(aTHX_ sen, declarator);
817                         *ptype = reify_type(aTHX_ sen, declarator, spec, *ptype);
818
819                         c = lex_peek_unichar(0);
820                 }
821         }
822
823         if (c == ':') {
824                 lex_read_unichar(0);
825                 lex_read_space(0);
826
827                 *pflags |= PARAM_NAMED;
828
829                 c = lex_peek_unichar(0);
830         }
831
832         if (c == -1) {
833                 croak("In %"SVf": unterminated parameter list", SVfARG(declarator));
834         }
835
836         if (!(c == '$' || c == '@' || c == '%')) {
837                 croak("In %"SVf": unexpected '%c' in parameter list (expecting a sigil)", SVfARG(declarator), (int)c);
838         }
839
840         sigil = c;
841
842         lex_read_unichar(0);
843         lex_read_space(0);
844
845         if (!(name = my_scan_word(aTHX_ sen, FALSE))) {
846                 croak("In %"SVf": missing identifier after '%c'", SVfARG(declarator), sigil);
847         }
848         sv_insert(name, 0, 0, &sigil, 1);
849         *pname = name;
850
851         lex_read_space(0);
852         c = lex_peek_unichar(0);
853
854         if (c == '=') {
855                 lex_read_unichar(0);
856                 lex_read_space(0);
857
858
859                 if (!param_spec->invocant.name && SvTRUE(spec->shift)) {
860                         param_spec->invocant.name = spec->shift;
861                         param_spec->invocant.padoff = pad_add_name_sv(param_spec->invocant.name, 0, NULL, NULL);
862                 }
863
864                 op_guard_update(ginit, parse_termexpr(0));
865
866                 lex_read_space(0);
867                 c = lex_peek_unichar(0);
868         }
869
870         if (c == ':') {
871                 *pflags |= PARAM_INVOCANT;
872                 lex_read_unichar(0);
873                 lex_read_space(0);
874         } else if (c == ',') {
875                 lex_read_unichar(0);
876                 lex_read_space(0);
877         } else if (c != ')') {
878                 if (c == -1) {
879                         croak("In %"SVf": unterminated parameter list", SVfARG(declarator));
880                 }
881                 croak("In %"SVf": unexpected '%c' in parameter list (expecting ',')", SVfARG(declarator), (int)c);
882         }
883
884         return pad_add_name_sv(*pname, IF_HAVE_PERL_5_16(padadd_NO_DUP_CHECK, 0), NULL, NULL);
885 }
886
887 static OP *my_var_g(pTHX_ I32 type, I32 flags, PADOFFSET padoff) {
888         OP *var = newOP(type, flags);
889         var->op_targ = padoff;
890         return var;
891 }
892
893 static OP *my_var(pTHX_ I32 flags, PADOFFSET padoff) {
894         return my_var_g(aTHX_ OP_PADSV, flags, padoff);
895 }
896
897 static OP *mkhvelem(pTHX_ PADOFFSET h, OP *k) {
898         OP *hv = my_var_g(aTHX_ OP_PADHV, OPf_REF, h);
899         return newBINOP(OP_HELEM, 0, hv, k);
900 }
901
902 static OP *mkconstsv(pTHX_ SV *sv) {
903         return newSVOP(OP_CONST, 0, sv);
904 }
905
906 static OP *mkconstiv(pTHX_ IV i) {
907         return mkconstsv(aTHX_ newSViv(i));
908 }
909
910 static OP *mkconstpv(pTHX_ const char *p, size_t n) {
911         return mkconstsv(aTHX_ newSVpv(p, n));
912 }
913
914 #define mkconstpvs(S) mkconstpv(aTHX_ "" S "", sizeof S - 1)
915
916 static OP *mktypecheck(pTHX_ const SV *declarator, int nr, SV *name, PADOFFSET padoff, SV *type) {
917         /* $type->check($value) or Carp::croak "...: " . $type->get_message($value) */
918         OP *chk, *err, *msg, *xcroak;
919
920         err = mkconstsv(aTHX_ newSVpvf("In %"SVf": parameter %d (%"SVf"): ", SVfARG(declarator), nr, SVfARG(name)));
921         {
922                 OP *args = NULL;
923
924                 args = op_append_elem(OP_LIST, args, mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(type)));
925                 args = op_append_elem(
926                         OP_LIST, args,
927                         padoff == NOT_IN_PAD
928                                 ? S_newDEFSVOP(aTHX)
929                                 : my_var(aTHX_ 0, padoff)
930                 );
931                 args = op_append_elem(OP_LIST, args, newUNOP(OP_METHOD, 0, mkconstpvs("get_message")));
932
933                 msg = args;
934                 msg->op_type = OP_ENTERSUB;
935                 msg->op_ppaddr = PL_ppaddr[OP_ENTERSUB];
936                 msg->op_flags |= OPf_STACKED;
937         }
938
939         msg = newBINOP(OP_CONCAT, 0, err, msg);
940
941         xcroak = newCVREF(
942                 OPf_WANT_SCALAR,
943                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
944         );
945         xcroak = newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, msg, xcroak));
946
947         {
948                 OP *args = NULL;
949
950                 args = op_append_elem(OP_LIST, args, mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(type)));
951                 args = op_append_elem(
952                         OP_LIST, args,
953                         padoff == NOT_IN_PAD
954                                 ? S_newDEFSVOP(aTHX)
955                                 : my_var(aTHX_ 0, padoff)
956                 );
957                 args = op_append_elem(OP_LIST, args, newUNOP(OP_METHOD, 0, mkconstpvs("check")));
958
959                 chk = args;
960                 chk->op_type = OP_ENTERSUB;
961                 chk->op_ppaddr = PL_ppaddr[OP_ENTERSUB];
962                 chk->op_flags |= OPf_STACKED;
963         }
964
965         chk = newLOGOP(OP_OR, 0, chk, xcroak);
966         return chk;
967 }
968
969 static OP *mktypecheckp(pTHX_ const SV *declarator, int nr, const Param *param) {
970         return mktypecheck(aTHX_ declarator, nr, param->name, param->padoff, param->type);
971 }
972
973 static void register_info(pTHX_ UV key, SV *declarator, const KWSpec *kws, const ParamSpec *ps) {
974         dSP;
975
976         ENTER;
977         SAVETMPS;
978
979         PUSHMARK(SP);
980         EXTEND(SP, 10);
981
982         /* 0 */ {
983                 mPUSHu(key);
984         }
985         /* 1 */ {
986                 STRLEN n;
987                 char *p = SvPV(declarator, n);
988                 char *q = memchr(p, ' ', n);
989                 SV *tmp = newSVpvn_utf8(p, q ? (size_t)(q - p) : n, SvUTF8(declarator));
990                 mPUSHs(tmp);
991         }
992         if (!ps) {
993                 if (SvTRUE(kws->shift)) {
994                         PUSHs(kws->shift);
995                 } else {
996                         PUSHmortal;
997                 }
998                 PUSHmortal;
999                 mPUSHs(newRV_noinc((SV *)newAV()));
1000                 mPUSHs(newRV_noinc((SV *)newAV()));
1001                 mPUSHs(newRV_noinc((SV *)newAV()));
1002                 mPUSHs(newRV_noinc((SV *)newAV()));
1003                 mPUSHp("@_", 2);
1004                 PUSHmortal;
1005         } else {
1006                 /* 2, 3 */ {
1007                         if (ps->invocant.name) {
1008                                 PUSHs(ps->invocant.name);
1009                                 if (ps->invocant.type) {
1010                                         PUSHs(ps->invocant.type);
1011                                 } else {
1012                                         PUSHmortal;
1013                                 }
1014                         } else {
1015                                 PUSHmortal;
1016                                 PUSHmortal;
1017                         }
1018                 }
1019                 /* 4 */ {
1020                         size_t i, lim;
1021                         AV *av;
1022
1023                         lim = ps->positional_required.used;
1024
1025                         av = newAV();
1026                         if (lim) {
1027                                 av_extend(av, (lim - 1) * 2);
1028                                 for (i = 0; i < lim; i++) {
1029                                         Param *cur = &ps->positional_required.data[i];
1030                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1031                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1032                                 }
1033                         }
1034
1035                         mPUSHs(newRV_noinc((SV *)av));
1036                 }
1037                 /* 5 */ {
1038                         size_t i, lim;
1039                         AV *av;
1040
1041                         lim = ps->positional_optional.used;
1042
1043                         av = newAV();
1044                         if (lim) {
1045                                 av_extend(av, (lim - 1) * 2);
1046                                 for (i = 0; i < lim; i++) {
1047                                         Param *cur = &ps->positional_optional.data[i].param;
1048                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1049                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1050                                 }
1051                         }
1052
1053                         mPUSHs(newRV_noinc((SV *)av));
1054                 }
1055                 /* 6 */ {
1056                         size_t i, lim;
1057                         AV *av;
1058
1059                         lim = ps->named_required.used;
1060
1061                         av = newAV();
1062                         if (lim) {
1063                                 av_extend(av, (lim - 1) * 2);
1064                                 for (i = 0; i < lim; i++) {
1065                                         Param *cur = &ps->named_required.data[i];
1066                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1067                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1068                                 }
1069                         }
1070
1071                         mPUSHs(newRV_noinc((SV *)av));
1072                 }
1073                 /* 7 */ {
1074                         size_t i, lim;
1075                         AV *av;
1076
1077                         lim = ps->named_optional.used;
1078
1079                         av = newAV();
1080                         if (lim) {
1081                                 av_extend(av, (lim - 1) * 2);
1082                                 for (i = 0; i < lim; i++) {
1083                                         Param *cur = &ps->named_optional.data[i].param;
1084                                         av_push(av, SvREFCNT_inc_simple_NN(cur->name));
1085                                         av_push(av, cur->type ? SvREFCNT_inc_simple_NN(cur->type) : &PL_sv_undef);
1086                                 }
1087                         }
1088
1089                         mPUSHs(newRV_noinc((SV *)av));
1090                 }
1091                 /* 8, 9 */ {
1092                         if (ps->slurpy.name) {
1093                                 PUSHs(ps->slurpy.name);
1094                                 if (ps->slurpy.type) {
1095                                         PUSHs(ps->slurpy.type);
1096                                 } else {
1097                                         PUSHmortal;
1098                                 }
1099                         } else {
1100                                 PUSHmortal;
1101                                 PUSHmortal;
1102                         }
1103                 }
1104         }
1105         PUTBACK;
1106
1107         call_pv(MY_PKG "::_register_info", G_VOID);
1108
1109         FREETMPS;
1110         LEAVE;
1111 }
1112
1113 static int parse_fun(pTHX_ Sentinel sen, OP **pop, const char *keyword_ptr, STRLEN keyword_len, const KWSpec *spec) {
1114         ParamSpec *param_spec;
1115         SV *declarator;
1116         I32 floor_ix;
1117         int save_ix;
1118         SV *saw_name;
1119         OpGuard *prelude_sentinel;
1120         SV *proto;
1121         OpGuard *attrs_sentinel;
1122         OP *body;
1123         unsigned builtin_attrs;
1124         I32 c;
1125
1126         declarator = sentinel_mortalize(sen, newSVpvn(keyword_ptr, keyword_len));
1127         if (lex_bufutf8()) {
1128                 SvUTF8_on(declarator);
1129         }
1130
1131         lex_read_space(0);
1132
1133         builtin_attrs = 0;
1134
1135         /* function name */
1136         saw_name = NULL;
1137         if ((spec->flags & FLAG_NAME_OK) && (saw_name = my_scan_word(aTHX_ sen, TRUE))) {
1138
1139                 if (PL_parser->expect != XSTATE) {
1140                         /* bail out early so we don't predeclare $saw_name */
1141                         croak("In %"SVf": I was expecting a function body, not \"%"SVf"\"", SVfARG(declarator), SVfARG(saw_name));
1142                 }
1143
1144                 sv_catpvs(declarator, " ");
1145                 sv_catsv(declarator, saw_name);
1146
1147                 if (
1148                         sv_eq_pvs(saw_name, "BEGIN") ||
1149                         sv_eq_pvs(saw_name, "END") ||
1150                         sv_eq_pvs(saw_name, "INIT") ||
1151                         sv_eq_pvs(saw_name, "CHECK") ||
1152                         sv_eq_pvs(saw_name, "UNITCHECK")
1153                 ) {
1154                         builtin_attrs |= MY_ATTR_SPECIAL;
1155                 }
1156
1157                 lex_read_space(0);
1158         } else if (!(spec->flags & FLAG_ANON_OK)) {
1159                 croak("I was expecting a function name, not \"%.*s\"", (int)(PL_parser->bufend - PL_parser->bufptr), PL_parser->bufptr);
1160         } else {
1161                 sv_catpvs(declarator, " (anon)");
1162         }
1163
1164         /* we're a subroutine declaration */
1165         floor_ix = start_subparse(FALSE, saw_name ? 0 : CVf_ANON);
1166         SAVEFREESV(PL_compcv);
1167
1168         /* create outer block: '{' */
1169         save_ix = S_block_start(aTHX_ TRUE);
1170
1171         /* initialize synthetic optree */
1172         Newx(prelude_sentinel, 1, OpGuard);
1173         op_guard_init(prelude_sentinel);
1174         sentinel_register(sen, prelude_sentinel, free_op_guard_void);
1175
1176         /* parameters */
1177         param_spec = NULL;
1178
1179         c = lex_peek_unichar(0);
1180         if (c == '(') {
1181                 OpGuard *init_sentinel;
1182
1183                 Newx(init_sentinel, 1, OpGuard);
1184                 op_guard_init(init_sentinel);
1185                 sentinel_register(sen, init_sentinel, free_op_guard_void);
1186
1187                 Newx(param_spec, 1, ParamSpec);
1188                 ps_init(param_spec);
1189                 sentinel_register(sen, param_spec, ps_free_void);
1190
1191                 lex_read_unichar(0);
1192                 lex_read_space(0);
1193
1194                 while ((c = lex_peek_unichar(0)) != ')') {
1195                         int flags;
1196                         SV *name, *type;
1197                         char sigil;
1198                         PADOFFSET padoff;
1199
1200                         padoff = parse_param(aTHX_ sen, declarator, spec, param_spec, &flags, &name, init_sentinel, &type);
1201
1202                         S_intro_my(aTHX);
1203
1204                         sigil = SvPV_nolen(name)[0];
1205
1206                         /* internal consistency */
1207                         if (flags & PARAM_NAMED) {
1208                                 if (flags & PARAM_INVOCANT) {
1209                                         croak("In %"SVf": invocant %"SVf" can't be a named parameter", SVfARG(declarator), SVfARG(name));
1210                                 }
1211                                 if (sigil != '$') {
1212                                         croak("In %"SVf": named parameter %"SVf" can't be a%s", SVfARG(declarator), SVfARG(name), sigil == '@' ? "n array" : " hash");
1213                                 }
1214                         } else if (flags & PARAM_INVOCANT) {
1215                                 if (init_sentinel->op) {
1216                                         croak("In %"SVf": invocant %"SVf" can't have a default value", SVfARG(declarator), SVfARG(name));
1217                                 }
1218                                 if (sigil != '$') {
1219                                         croak("In %"SVf": invocant %"SVf" can't be a%s", SVfARG(declarator), SVfARG(name), sigil == '@' ? "n array" : " hash");
1220                                 }
1221                         } else if (sigil != '$' && init_sentinel->op) {
1222                                 croak("In %"SVf": %s %"SVf" can't have a default value", SVfARG(declarator), sigil == '@' ? "array" : "hash", SVfARG(name));
1223                         }
1224
1225                         /* external constraints */
1226                         if (param_spec->slurpy.name) {
1227                                 croak("In %"SVf": I was expecting \")\" after \"%"SVf"\", not \"%"SVf"\"", SVfARG(declarator), SVfARG(param_spec->slurpy.name), SVfARG(name));
1228                         }
1229                         if (sigil != '$') {
1230                                 assert(!init_sentinel->op);
1231                                 param_spec->slurpy.name = name;
1232                                 param_spec->slurpy.padoff = padoff;
1233                                 param_spec->slurpy.type = type;
1234                                 continue;
1235                         }
1236
1237                         if (!(flags & PARAM_NAMED) && count_named_params(param_spec)) {
1238                                 croak("In %"SVf": positional parameter %"SVf" can't appear after named parameter %"SVf"", SVfARG(declarator), SVfARG(name), SVfARG((param_spec->named_required.used ? param_spec->named_required.data[0] : param_spec->named_optional.data[0].param).name));
1239                         }
1240
1241                         if (flags & PARAM_INVOCANT) {
1242                                 if (param_spec->invocant.name) {
1243                                         croak("In %"SVf": invalid double invocants %"SVf", %"SVf"", SVfARG(declarator), SVfARG(param_spec->invocant.name), SVfARG(name));
1244                                 }
1245                                 if (count_positional_params(param_spec) || count_named_params(param_spec)) {
1246                                         croak("In %"SVf": invocant %"SVf" must be first in parameter list", SVfARG(declarator), SVfARG(name));
1247                                 }
1248                                 if (!(spec->flags & FLAG_INVOCANT)) {
1249                                         croak("In %"SVf": invocant %"SVf" not allowed here", SVfARG(declarator), SVfARG(name));
1250                                 }
1251                                 param_spec->invocant.name = name;
1252                                 param_spec->invocant.padoff = padoff;
1253                                 param_spec->invocant.type = type;
1254                                 continue;
1255                         }
1256
1257                         if (init_sentinel->op && !(spec->flags & FLAG_DEFAULT_ARGS)) {
1258                                 croak("In %"SVf": default argument for %"SVf" not allowed here", SVfARG(declarator), SVfARG(name));
1259                         }
1260
1261                         if (ps_contains(aTHX_ param_spec, name)) {
1262                                 croak("In %"SVf": %"SVf" can't appear twice in the same parameter list", SVfARG(declarator), SVfARG(name));
1263                         }
1264
1265                         if (flags & PARAM_NAMED) {
1266                                 if (!(spec->flags & FLAG_NAMED_PARAMS)) {
1267                                         croak("In %"SVf": named parameter :%"SVf" not allowed here", SVfARG(declarator), SVfARG(name));
1268                                 }
1269
1270                                 if (init_sentinel->op) {
1271                                         ParamInit *pi = piv_extend(&param_spec->named_optional);
1272                                         pi->param.name = name;
1273                                         pi->param.padoff = padoff;
1274                                         pi->param.type = type;
1275                                         pi->init = op_guard_transfer(init_sentinel);
1276                                         param_spec->named_optional.used++;
1277                                 } else {
1278                                         Param *p;
1279
1280                                         if (param_spec->positional_optional.used) {
1281                                                 croak("In %"SVf": can't combine optional positional (%"SVf") and required named (%"SVf") parameters", SVfARG(declarator), SVfARG(param_spec->positional_optional.data[0].param.name), SVfARG(name));
1282                                         }
1283
1284                                         p = pv_extend(&param_spec->named_required);
1285                                         p->name = name;
1286                                         p->padoff = padoff;
1287                                         p->type = type;
1288                                         param_spec->named_required.used++;
1289                                 }
1290                         } else {
1291                                 if (init_sentinel->op || param_spec->positional_optional.used) {
1292                                         ParamInit *pi = piv_extend(&param_spec->positional_optional);
1293                                         pi->param.name = name;
1294                                         pi->param.padoff = padoff;
1295                                         pi->param.type = type;
1296                                         pi->init = op_guard_transfer(init_sentinel);
1297                                         param_spec->positional_optional.used++;
1298                                 } else {
1299                                         Param *p = pv_extend(&param_spec->positional_required);
1300                                         p->name = name;
1301                                         p->padoff = padoff;
1302                                         p->type = type;
1303                                         param_spec->positional_required.used++;
1304                                 }
1305                         }
1306
1307                 }
1308                 lex_read_unichar(0);
1309                 lex_read_space(0);
1310
1311                 if (!param_spec->invocant.name && SvTRUE(spec->shift)) {
1312                         if (ps_contains(aTHX_ param_spec, spec->shift)) {
1313                                 croak("In %"SVf": %"SVf" can't appear twice in the same parameter list", SVfARG(declarator), SVfARG(spec->shift));
1314                         }
1315
1316                         param_spec->invocant.name = spec->shift;
1317                         param_spec->invocant.padoff = pad_add_name_sv(param_spec->invocant.name, 0, NULL, NULL);
1318                 }
1319         }
1320
1321         /* prototype */
1322         proto = NULL;
1323         c = lex_peek_unichar(0);
1324         if (c == ':') {
1325                 lex_read_unichar(0);
1326                 lex_read_space(0);
1327
1328                 c = lex_peek_unichar(0);
1329                 if (c != '(') {
1330                         lex_stuff_pvs(":", 0);
1331                         c = ':';
1332                 } else {
1333                         lex_read_unichar(0);
1334                         if (!(proto = my_scan_parens_tail(aTHX_ sen, FALSE))) {
1335                                 croak("In %"SVf": prototype not terminated", SVfARG(declarator));
1336                         }
1337                         my_check_prototype(aTHX_ sen, declarator, proto);
1338                         lex_read_space(0);
1339                         c = lex_peek_unichar(0);
1340                         if (!(c == ':' || c == '{')) {
1341                                 lex_stuff_pvs(":", 0);
1342                                 c = ':';
1343                         }
1344                 }
1345         }
1346
1347         /* attributes */
1348         Newx(attrs_sentinel, 1, OpGuard);
1349         op_guard_init(attrs_sentinel);
1350         sentinel_register(sen, attrs_sentinel, free_op_guard_void);
1351
1352         if (c == ':' || c == '{') /* '}' - hi, vim */ {
1353
1354                 /* kludge default attributes in */
1355                 if (SvTRUE(spec->attrs) && SvPV_nolen(spec->attrs)[0] == ':') {
1356                         lex_stuff_sv(spec->attrs, 0);
1357                         c = ':';
1358                 }
1359
1360                 if (c == ':') {
1361                         lex_read_unichar(0);
1362                         lex_read_space(0);
1363                         c = lex_peek_unichar(0);
1364
1365                         for (;;) {
1366                                 SV *attr;
1367
1368                                 if (!(attr = my_scan_word(aTHX_ sen, FALSE))) {
1369                                         break;
1370                                 }
1371
1372                                 lex_read_space(0);
1373                                 c = lex_peek_unichar(0);
1374
1375                                 if (c != '(') {
1376                                         if (sv_eq_pvs(attr, "lvalue")) {
1377                                                 builtin_attrs |= MY_ATTR_LVALUE;
1378                                                 attr = NULL;
1379                                         } else if (sv_eq_pvs(attr, "method")) {
1380                                                 builtin_attrs |= MY_ATTR_METHOD;
1381                                                 attr = NULL;
1382                                         }
1383                                 } else {
1384                                         SV *sv;
1385                                         lex_read_unichar(0);
1386                                         if (!(sv = my_scan_parens_tail(aTHX_ sen, TRUE))) {
1387                                                 croak("In %"SVf": unterminated attribute parameter in attribute list", SVfARG(declarator));
1388                                         }
1389                                         sv_catpvs(attr, "(");
1390                                         sv_catsv(attr, sv);
1391                                         sv_catpvs(attr, ")");
1392
1393                                         lex_read_space(0);
1394                                         c = lex_peek_unichar(0);
1395                                 }
1396
1397                                 if (attr) {
1398                                         op_guard_update(attrs_sentinel, op_append_elem(OP_LIST, attrs_sentinel->op, mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(attr))));
1399                                 }
1400
1401                                 if (c == ':') {
1402                                         lex_read_unichar(0);
1403                                         lex_read_space(0);
1404                                         c = lex_peek_unichar(0);
1405                                 }
1406                         }
1407                 }
1408         }
1409
1410         /* body */
1411         if (c != '{') /* '}' - hi, vim */ {
1412                 croak("In %"SVf": I was expecting a function body, not \"%c\"", SVfARG(declarator), (int)c);
1413         }
1414
1415         /* surprise predeclaration! */
1416         if (saw_name) {
1417                 /* 'sub NAME (PROTO);' to make name/proto known to perl before it
1418                    starts parsing the body */
1419                 const I32 sub_ix = start_subparse(FALSE, 0);
1420                 SAVEFREESV(PL_compcv);
1421
1422                 SvREFCNT_inc_simple_void(PL_compcv);
1423
1424                 newATTRSUB(
1425                         sub_ix,
1426                         mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(saw_name)),
1427                         proto ? mkconstsv(aTHX_ SvREFCNT_inc_simple_NN(proto)) : NULL,
1428                         NULL,
1429                         NULL
1430                 );
1431         }
1432
1433         if (builtin_attrs & MY_ATTR_LVALUE) {
1434                 CvLVALUE_on(PL_compcv);
1435         }
1436         if (builtin_attrs & MY_ATTR_METHOD) {
1437                 CvMETHOD_on(PL_compcv);
1438         }
1439         if (builtin_attrs & MY_ATTR_SPECIAL) {
1440                 CvSPECIAL_on(PL_compcv);
1441         }
1442
1443         /* check number of arguments */
1444         if (spec->flags & FLAG_CHECK_NARGS) {
1445                 int amin, amax;
1446
1447                 amin = args_min(aTHX_ param_spec, spec);
1448                 if (amin > 0) {
1449                         OP *chk, *cond, *err, *xcroak;
1450
1451                         err = mkconstsv(aTHX_ newSVpvf("Not enough arguments for %"SVf" (expected %d, got ", SVfARG(declarator), amin));
1452                         err = newBINOP(
1453                                 OP_CONCAT, 0,
1454                                 err,
1455                                 newAVREF(newGVOP(OP_GV, 0, PL_defgv))
1456                         );
1457                         err = newBINOP(
1458                                 OP_CONCAT, 0,
1459                                 err,
1460                                 mkconstpvs(")")
1461                         );
1462
1463                         xcroak = newCVREF(OPf_WANT_SCALAR,
1464                                           newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV)));
1465                         err = newUNOP(OP_ENTERSUB, OPf_STACKED,
1466                                       op_append_elem(OP_LIST, err, xcroak));
1467
1468                         cond = newBINOP(OP_LT, 0,
1469                                         newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1470                                         mkconstiv(aTHX_ amin));
1471                         chk = newLOGOP(OP_AND, 0, cond, err);
1472
1473                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, chk)));
1474                 }
1475
1476                 amax = args_max(param_spec);
1477                 if (amax >= 0) {
1478                         OP *chk, *cond, *err, *xcroak;
1479
1480                         err = mkconstsv(aTHX_ newSVpvf("Too many arguments for %"SVf" (expected %d, got ", SVfARG(declarator), amax));
1481                         err = newBINOP(
1482                                 OP_CONCAT, 0,
1483                                 err,
1484                                 newAVREF(newGVOP(OP_GV, 0, PL_defgv))
1485                         );
1486                         err = newBINOP(
1487                                 OP_CONCAT, 0,
1488                                 err,
1489                                 mkconstpvs(")")
1490                         );
1491
1492                         xcroak = newCVREF(
1493                                 OPf_WANT_SCALAR,
1494                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1495                         );
1496                         err = newUNOP(OP_ENTERSUB, OPf_STACKED,
1497                         op_append_elem(OP_LIST, err, xcroak));
1498
1499                         cond = newBINOP(
1500                                 OP_GT, 0,
1501                                 newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1502                                 mkconstiv(aTHX_ amax)
1503                         );
1504                         chk = newLOGOP(OP_AND, 0, cond, err);
1505
1506                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, chk)));
1507                 }
1508
1509                 if (param_spec && (count_named_params(param_spec) || (param_spec->slurpy.name && SvPV_nolen(param_spec->slurpy.name)[0] == '%'))) {
1510                         OP *chk, *cond, *err, *xcroak;
1511                         const UV fixed = count_positional_params(param_spec) + !!param_spec->invocant.name;
1512
1513                         err = mkconstsv(aTHX_ newSVpvf("Odd number of paired arguments for %"SVf"", SVfARG(declarator)));
1514
1515                         xcroak = newCVREF(
1516                                 OPf_WANT_SCALAR,
1517                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1518                         );
1519                         err = newUNOP(OP_ENTERSUB, OPf_STACKED,
1520                         op_append_elem(OP_LIST, err, xcroak));
1521
1522                         cond = newBINOP(OP_GT, 0,
1523                                         newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1524                                         mkconstiv(aTHX_ fixed));
1525                         cond = newLOGOP(OP_AND, 0,
1526                                         cond,
1527                                         newBINOP(OP_MODULO, 0,
1528                                                  fixed
1529                                                  ? newBINOP(OP_SUBTRACT, 0,
1530                                                             newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1531                                                             mkconstiv(aTHX_ fixed))
1532                                                  : newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1533                                                  mkconstiv(aTHX_ 2)));
1534                         chk = newLOGOP(OP_AND, 0, cond, err);
1535
1536                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, chk)));
1537                 }
1538         }
1539
1540         if (!param_spec) {
1541                 /* my $invocant = shift; */
1542                 if (SvTRUE(spec->shift)) {
1543                         OP *var;
1544
1545                         var = my_var(
1546                                 aTHX_
1547                                 OPf_MOD | (OPpLVAL_INTRO << 8),
1548                                 pad_add_name_sv(spec->shift, 0, NULL, NULL)
1549                         );
1550                         var = newASSIGNOP(OPf_STACKED, var, 0, newOP(OP_SHIFT, 0));
1551
1552                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1553                 }
1554         } else {
1555                 /* my $invocant = shift; */
1556                 if (param_spec->invocant.name) {
1557                         OP *var;
1558
1559                         var = my_var(
1560                                 aTHX_
1561                                 OPf_MOD | (OPpLVAL_INTRO << 8),
1562                                 param_spec->invocant.padoff
1563                         );
1564                         var = newASSIGNOP(OPf_STACKED, var, 0, newOP(OP_SHIFT, 0));
1565
1566                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1567
1568                         if (param_spec->invocant.type && (spec->flags & FLAG_CHECK_TARGS)) {
1569                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, 0, &param_spec->invocant))));
1570                         }
1571                 }
1572
1573                 /* my (...) = @_; */
1574                 {
1575                         OP *lhs;
1576                         size_t i, lim;
1577
1578                         lhs = NULL;
1579
1580                         for (i = 0, lim = param_spec->positional_required.used; i < lim; i++) {
1581                                 OP *const var = my_var(
1582                                         aTHX_
1583                                         OPf_WANT_LIST | (OPpLVAL_INTRO << 8),
1584                                         param_spec->positional_required.data[i].padoff
1585                                 );
1586                                 lhs = op_append_elem(OP_LIST, lhs, var);
1587                         }
1588
1589                         for (i = 0, lim = param_spec->positional_optional.used; i < lim; i++) {
1590                                 OP *const var = my_var(
1591                                         aTHX_
1592                                         OPf_WANT_LIST | (OPpLVAL_INTRO << 8),
1593                                         param_spec->positional_optional.data[i].param.padoff
1594                                 );
1595                                 lhs = op_append_elem(OP_LIST, lhs, var);
1596                         }
1597
1598                         {
1599                                 PADOFFSET padoff;
1600                                 I32 type;
1601                                 bool slurpy_hash;
1602
1603                                 /*
1604                                  * cases:
1605                                  *  1) no named params
1606                                  *   1.1) slurpy
1607                                  *       => put it in
1608                                  *   1.2) no slurpy
1609                                  *       => nop
1610                                  *  2) named params
1611                                  *   2.1) no slurpy
1612                                  *       => synthetic %{rest}
1613                                  *   2.2) slurpy is a hash
1614                                  *       => put it in
1615                                  *   2.3) slurpy is an array
1616                                  *       => synthetic %{rest}
1617                                  *          remember to declare array later
1618                                  */
1619
1620                                 slurpy_hash = param_spec->slurpy.name && SvPV_nolen(param_spec->slurpy.name)[0] == '%';
1621                                 if (!count_named_params(param_spec)) {
1622                                         if (param_spec->slurpy.name) {
1623                                                 padoff = param_spec->slurpy.padoff;
1624                                                 type = slurpy_hash ? OP_PADHV : OP_PADAV;
1625                                         } else {
1626                                                 padoff = NOT_IN_PAD;
1627                                                 type = OP_PADSV;
1628                                         }
1629                                 } else if (slurpy_hash) {
1630                                         padoff = param_spec->slurpy.padoff;
1631                                         type = OP_PADHV;
1632                                 } else {
1633                                         padoff = param_spec->rest_hash = pad_add_name_pvs("%{rest}", 0, NULL, NULL);
1634                                         type = OP_PADHV;
1635                                 }
1636
1637                                 if (padoff != NOT_IN_PAD) {
1638                                         OP *const var = my_var_g(
1639                                                 aTHX_
1640                                                 type,
1641                                                 OPf_WANT_LIST | (OPpLVAL_INTRO << 8),
1642                                                 padoff
1643                                         );
1644
1645                                         lhs = op_append_elem(OP_LIST, lhs, var);
1646
1647                                         if (type == OP_PADHV) {
1648                                                 param_spec->rest_hash = padoff;
1649                                         }
1650                                 }
1651                         }
1652
1653                         if (lhs) {
1654                                 OP *rhs;
1655                                 lhs->op_flags |= OPf_PARENS;
1656                                 rhs = newAVREF(newGVOP(OP_GV, 0, PL_defgv));
1657
1658                                 op_guard_update(prelude_sentinel, op_append_list(
1659                                         OP_LINESEQ, prelude_sentinel->op,
1660                                         newSTATEOP(
1661                                                 0, NULL,
1662                                                 newASSIGNOP(OPf_STACKED, lhs, 0, rhs)
1663                                         )
1664                                 ));
1665                         }
1666                 }
1667
1668                 /* default positional arguments */
1669                 {
1670                         size_t i, lim, req;
1671                         OP *nest;
1672
1673                         nest = NULL;
1674
1675                         req = param_spec->positional_required.used;
1676                         for (i = 0, lim = param_spec->positional_optional.used; i < lim; i++) {
1677                                 ParamInit *cur = &param_spec->positional_optional.data[i];
1678                                 OP *var, *cond;
1679
1680                                 cond = newBINOP(
1681                                         OP_LT, 0,
1682                                         newAVREF(newGVOP(OP_GV, 0, PL_defgv)),
1683                                         mkconstiv(aTHX_ req + i + 1)
1684                                 );
1685
1686                                 var = my_var(aTHX_ 0, cur->param.padoff);
1687
1688                                 nest = op_append_list(
1689                                         OP_LINESEQ, nest,
1690                                         newASSIGNOP(OPf_STACKED, var, 0, op_guard_relinquish(&cur->init))
1691                                 );
1692                                 nest = newCONDOP(
1693                                         0,
1694                                         cond,
1695                                         nest,
1696                                         NULL
1697                                 );
1698                         }
1699
1700                         op_guard_update(prelude_sentinel, op_append_list(
1701                                 OP_LINESEQ, prelude_sentinel->op,
1702                                 nest
1703                         ));
1704                 }
1705
1706                 /* named parameters */
1707                 if (count_named_params(param_spec)) {
1708                         size_t i, lim;
1709
1710                         assert(param_spec->rest_hash != NOT_IN_PAD);
1711
1712                         for (i = 0, lim = param_spec->named_required.used; i < lim; i++) {
1713                                 Param *cur = &param_spec->named_required.data[i];
1714                                 size_t n;
1715                                 char *p = SvPV(cur->name, n);
1716                                 OP *var, *cond;
1717
1718                                 cond = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1719
1720                                 if (spec->flags & FLAG_CHECK_NARGS) {
1721                                         OP *xcroak, *msg;
1722
1723                                         var = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1724                                         var = newUNOP(OP_DELETE, 0, var);
1725
1726                                         msg = mkconstsv(aTHX_ newSVpvf("In %"SVf": missing named parameter: %.*s", SVfARG(declarator), (int)(n - 1), p + 1));
1727                                         xcroak = newCVREF(
1728                                                 OPf_WANT_SCALAR,
1729                                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1730                                         );
1731                                         xcroak = newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, msg, xcroak));
1732
1733                                         cond = newUNOP(OP_EXISTS, 0, cond);
1734
1735                                         cond = newCONDOP(0, cond, var, xcroak);
1736                                 }
1737
1738                                 var = my_var(
1739                                         aTHX_
1740                                         OPf_MOD | (OPpLVAL_INTRO << 8),
1741                                         cur->padoff
1742                                 );
1743                                 var = newASSIGNOP(OPf_STACKED, var, 0, cond);
1744
1745                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1746                         }
1747
1748                         for (i = 0, lim = param_spec->named_optional.used; i < lim; i++) {
1749                                 ParamInit *cur = &param_spec->named_optional.data[i];
1750                                 size_t n;
1751                                 char *p = SvPV(cur->param.name, n);
1752                                 OP *var, *cond;
1753
1754                                 var = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1755                                 var = newUNOP(OP_DELETE, 0, var);
1756
1757                                 cond = mkhvelem(aTHX_ param_spec->rest_hash, mkconstpv(aTHX_ p + 1, n - 1));
1758                                 cond = newUNOP(OP_EXISTS, 0, cond);
1759
1760                                 cond = newCONDOP(0, cond, var, op_guard_relinquish(&cur->init));
1761
1762                                 var = my_var(
1763                                         aTHX_
1764                                         OPf_MOD | (OPpLVAL_INTRO << 8),
1765                                         cur->param.padoff
1766                                 );
1767                                 var = newASSIGNOP(OPf_STACKED, var, 0, cond);
1768
1769                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1770                         }
1771
1772                         if (!param_spec->slurpy.name) {
1773                                 if (spec->flags & FLAG_CHECK_NARGS) {
1774                                         /* croak if %{rest} */
1775                                         OP *xcroak, *cond, *keys, *msg;
1776
1777                                         keys = newUNOP(OP_KEYS, 0, my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash));
1778                                         keys = newLISTOP(OP_SORT, 0, newOP(OP_PUSHMARK, 0), keys);
1779                                         {
1780                                                 OP *first, *mid, *last;
1781
1782                                                 last = keys;
1783
1784                                                 mid = mkconstpvs(", ");
1785                                                 mid->op_sibling = last;
1786
1787                                                 first = newOP(OP_PUSHMARK, 0);
1788
1789                                                 keys = newLISTOP(OP_JOIN, 0, first, mid);
1790                                                 keys->op_targ = pad_alloc(OP_JOIN, SVs_PADTMP);
1791                                                 ((LISTOP *)keys)->op_last = last;
1792                                         }
1793
1794                                         msg = mkconstsv(aTHX_ newSVpvf("In %"SVf": no such named parameter: ", SVfARG(declarator)));
1795                                         msg = newBINOP(OP_CONCAT, 0, msg, keys);
1796
1797                                         xcroak = newCVREF(
1798                                                 OPf_WANT_SCALAR,
1799                                                 newGVOP(OP_GV, 0, gv_fetchpvs("Carp::croak", 0, SVt_PVCV))
1800                                         );
1801                                         xcroak = newUNOP(OP_ENTERSUB, OPf_STACKED, op_append_elem(OP_LIST, msg, xcroak));
1802
1803                                         cond = newUNOP(OP_KEYS, 0, my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash));
1804                                         xcroak = newCONDOP(0, cond, xcroak, NULL);
1805
1806                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, xcroak)));
1807                                 } else {
1808                                         OP *clear;
1809
1810                                         clear = newASSIGNOP(
1811                                                 OPf_STACKED,
1812                                                 my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash),
1813                                                 0,
1814                                                 newNULLLIST()
1815                                         );
1816
1817                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, clear)));
1818                                 }
1819                         } else if (param_spec->slurpy.padoff != param_spec->rest_hash) {
1820                                 OP *var, *clear;
1821
1822                                 assert(SvPV_nolen(param_spec->slurpy.name)[0] == '@');
1823
1824                                 var = my_var_g(
1825                                         aTHX_
1826                                         OP_PADAV,
1827                                         OPf_MOD | (OPpLVAL_INTRO << 8),
1828                                         param_spec->slurpy.padoff
1829                                 );
1830
1831                                 var = newASSIGNOP(OPf_STACKED, var, 0, my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash));
1832
1833                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, var)));
1834
1835                                 clear = newASSIGNOP(
1836                                         OPf_STACKED,
1837                                         my_var_g(aTHX_ OP_PADHV, 0, param_spec->rest_hash),
1838                                         0,
1839                                         newNULLLIST()
1840                                 );
1841
1842                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, clear)));
1843                         }
1844                 }
1845
1846                 if (spec->flags & FLAG_CHECK_TARGS) {
1847                         size_t i, lim, base;
1848
1849                         base = 1;
1850                         for (i = 0, lim = param_spec->positional_required.used; i < lim; i++) {
1851                                 Param *cur = &param_spec->positional_required.data[i];
1852
1853                                 if (cur->type) {
1854                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1855                                 }
1856                         }
1857                         base += i;
1858
1859                         for (i = 0, lim = param_spec->positional_optional.used; i < lim; i++) {
1860                                 Param *cur = &param_spec->positional_optional.data[i].param;
1861
1862                                 if (cur->type) {
1863                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1864                                 }
1865                         }
1866                         base += i;
1867
1868                         for (i = 0, lim = param_spec->named_required.used; i < lim; i++) {
1869                                 Param *cur = &param_spec->named_required.data[i];
1870
1871                                 if (cur->type) {
1872                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1873                                 }
1874                         }
1875                         base += i;
1876
1877                         for (i = 0, lim = param_spec->named_optional.used; i < lim; i++) {
1878                                 Param *cur = &param_spec->named_optional.data[i].param;
1879
1880                                 if (cur->type) {
1881                                         op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, mktypecheckp(aTHX_ declarator, base + i, cur))));
1882                                 }
1883                         }
1884                         base += i;
1885
1886                         if (param_spec->slurpy.type) {
1887                                 /* $type->valid($_) or croak $type->get_message($_) for @rest / values %rest */
1888                                 OP *check, *list, *loop;
1889
1890                                 check = mktypecheck(aTHX_ declarator, base, param_spec->slurpy.name, NOT_IN_PAD, param_spec->slurpy.type);
1891
1892                                 if (SvPV_nolen(param_spec->slurpy.name)[0] == '@') {
1893                                         list = my_var_g(aTHX_ OP_PADAV, 0, param_spec->slurpy.padoff);
1894                                 } else {
1895                                         list = my_var_g(aTHX_ OP_PADHV, 0, param_spec->slurpy.padoff);
1896                                         list = newUNOP(OP_VALUES, 0, list);
1897                                 }
1898
1899                                 loop = newFOROP(0, NULL, list, check, NULL);
1900
1901                                 op_guard_update(prelude_sentinel, op_append_list(OP_LINESEQ, prelude_sentinel->op, newSTATEOP(0, NULL, loop)));
1902                         }
1903                 }
1904         }
1905
1906         /* finally let perl parse the actual subroutine body */
1907         body = parse_block(0);
1908
1909         /* add '();' to make function return nothing by default */
1910         /* (otherwise the invisible parameter initialization can "leak" into
1911            the return value: fun ($x) {}->("asdf", 0) == 2) */
1912         if (prelude_sentinel->op) {
1913                 body = newSTATEOP(0, NULL, body);
1914         }
1915
1916         body = op_append_list(OP_LINESEQ, op_guard_relinquish(prelude_sentinel), body);
1917
1918         /* it's go time. */
1919         {
1920                 CV *cv;
1921                 OP *const attrs = op_guard_relinquish(attrs_sentinel);
1922
1923                 SvREFCNT_inc_simple_void(PL_compcv);
1924
1925                 /* close outer block: '}' */
1926                 S_block_end(aTHX_ save_ix, body);
1927
1928                 cv = newATTRSUB(
1929                         floor_ix,
1930                         saw_name ? newSVOP(OP_CONST, 0, SvREFCNT_inc_simple_NN(saw_name)) : NULL,
1931                         proto ? newSVOP(OP_CONST, 0, SvREFCNT_inc_simple_NN(proto)) : NULL,
1932                         attrs,
1933                         body
1934                 );
1935
1936                 if (cv) {
1937                         register_info(aTHX_ PTR2UV(CvROOT(cv)), declarator, spec, param_spec);
1938                 }
1939
1940                 if (saw_name) {
1941                         *pop = newOP(OP_NULL, 0);
1942                         return KEYWORD_PLUGIN_STMT;
1943                 }
1944
1945                 *pop = newUNOP(
1946                         OP_REFGEN, 0,
1947                         newSVOP(
1948                                 OP_ANONCODE, 0,
1949                                 (SV *)cv
1950                         )
1951                 );
1952                 return KEYWORD_PLUGIN_EXPR;
1953         }
1954 }
1955
1956 static int kw_flags_enter(pTHX_ Sentinel sen, const char *kw_ptr, STRLEN kw_len, KWSpec *spec) {
1957         HV *hints;
1958         SV *sv, **psv;
1959         const char *p, *kw_active;
1960         STRLEN kw_active_len;
1961         bool kw_is_utf8;
1962
1963         if (!(hints = GvHV(PL_hintgv))) {
1964                 return FALSE;
1965         }
1966         if (!(psv = hv_fetchs(hints, HINTK_KEYWORDS, 0))) {
1967                 return FALSE;
1968         }
1969         sv = *psv;
1970         kw_active = SvPV(sv, kw_active_len);
1971         if (kw_active_len <= kw_len) {
1972                 return FALSE;
1973         }
1974
1975         kw_is_utf8 = lex_bufutf8();
1976
1977         for (
1978                 p = kw_active;
1979                 (p = strchr(p, *kw_ptr)) &&
1980                 p < kw_active + kw_active_len - kw_len;
1981                 p++
1982         ) {
1983                 if (
1984                         (p == kw_active || p[-1] == ' ') &&
1985                         p[kw_len] == ' ' &&
1986                         memcmp(kw_ptr, p, kw_len) == 0
1987                 ) {
1988                         ENTER;
1989                         SAVETMPS;
1990
1991                         SAVEDESTRUCTOR_X(sentinel_clear_void, sen);
1992
1993                         spec->flags = 0;
1994                         spec->reify_type = 0;
1995                         spec->shift = sentinel_mortalize(sen, newSVpvs(""));
1996                         spec->attrs = sentinel_mortalize(sen, newSVpvs(""));
1997
1998 #define FETCH_HINTK_INTO(NAME, PTR, LEN, X) STMT_START { \
1999         const char *fk_ptr_; \
2000         STRLEN fk_len_; \
2001         I32 fk_xlen_; \
2002         SV *fk_sv_; \
2003         fk_sv_ = sentinel_mortalize(sen, newSVpvs(HINTK_ ## NAME)); \
2004         sv_catpvn(fk_sv_, PTR, LEN); \
2005         fk_ptr_ = SvPV(fk_sv_, fk_len_); \
2006         fk_xlen_ = fk_len_; \
2007         if (kw_is_utf8) { \
2008                 fk_xlen_ = -fk_xlen_; \
2009         } \
2010         if (!((X) = hv_fetch(hints, fk_ptr_, fk_xlen_, 0))) { \
2011                 croak("%s: internal error: $^H{'%.*s'} not set", MY_PKG, (int)fk_len_, fk_ptr_); \
2012         } \
2013 } STMT_END
2014
2015                         FETCH_HINTK_INTO(FLAGS_, kw_ptr, kw_len, psv);
2016                         spec->flags = SvIV(*psv);
2017
2018                         FETCH_HINTK_INTO(REIFY_, kw_ptr, kw_len, psv);
2019                         spec->reify_type = SvIV(*psv);
2020
2021                         FETCH_HINTK_INTO(SHIFT_, kw_ptr, kw_len, psv);
2022                         SvSetSV(spec->shift, *psv);
2023
2024                         FETCH_HINTK_INTO(ATTRS_, kw_ptr, kw_len, psv);
2025                         SvSetSV(spec->attrs, *psv);
2026
2027 #undef FETCH_HINTK_INTO
2028                         return TRUE;
2029                 }
2030         }
2031         return FALSE;
2032 }
2033
2034 static int my_keyword_plugin(pTHX_ char *keyword_ptr, STRLEN keyword_len, OP **op_ptr) {
2035         Sentinel sen = { NULL };
2036         KWSpec spec;
2037         int ret;
2038
2039         if (kw_flags_enter(aTHX_ sen, keyword_ptr, keyword_len, &spec)) {
2040                 /* scope was entered, 'sen' and 'spec' are initialized */
2041                 ret = parse_fun(aTHX_ sen, op_ptr, keyword_ptr, keyword_len, &spec);
2042                 FREETMPS;
2043                 LEAVE;
2044         } else {
2045                 /* not one of our keywords, no allocation done */
2046                 ret = next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr);
2047         }
2048
2049         return ret;
2050 }
2051
2052 WARNINGS_RESET
2053
2054 MODULE = Function::Parameters   PACKAGE = Function::Parameters   PREFIX = fp_
2055 PROTOTYPES: ENABLE
2056
2057 UV
2058 fp__cv_root(sv)
2059         SV * sv
2060         PREINIT:
2061                 CV *xcv;
2062                 HV *hv;
2063                 GV *gv;
2064         CODE:
2065                 xcv = sv_2cv(sv, &hv, &gv, 0);
2066                 RETVAL = PTR2UV(xcv ? CvROOT(xcv) : NULL);
2067         OUTPUT:
2068                 RETVAL
2069
2070 BOOT:
2071 WARNINGS_ENABLE {
2072         HV *const stash = gv_stashpvs(MY_PKG, GV_ADD);
2073         /**/
2074         newCONSTSUB(stash, "FLAG_NAME_OK",      newSViv(FLAG_NAME_OK));
2075         newCONSTSUB(stash, "FLAG_ANON_OK",      newSViv(FLAG_ANON_OK));
2076         newCONSTSUB(stash, "FLAG_DEFAULT_ARGS", newSViv(FLAG_DEFAULT_ARGS));
2077         newCONSTSUB(stash, "FLAG_CHECK_NARGS",  newSViv(FLAG_CHECK_NARGS));
2078         newCONSTSUB(stash, "FLAG_INVOCANT",     newSViv(FLAG_INVOCANT));
2079         newCONSTSUB(stash, "FLAG_NAMED_PARAMS", newSViv(FLAG_NAMED_PARAMS));
2080         newCONSTSUB(stash, "FLAG_TYPES_OK",     newSViv(FLAG_TYPES_OK));
2081         newCONSTSUB(stash, "FLAG_CHECK_TARGS",  newSViv(FLAG_CHECK_TARGS));
2082         newCONSTSUB(stash, "HINTK_KEYWORDS", newSVpvs(HINTK_KEYWORDS));
2083         newCONSTSUB(stash, "HINTK_FLAGS_",   newSVpvs(HINTK_FLAGS_));
2084         newCONSTSUB(stash, "HINTK_SHIFT_",   newSVpvs(HINTK_SHIFT_));
2085         newCONSTSUB(stash, "HINTK_ATTRS_",   newSVpvs(HINTK_ATTRS_));
2086         newCONSTSUB(stash, "HINTK_REIFY_",   newSVpvs(HINTK_REIFY_));
2087         /**/
2088         next_keyword_plugin = PL_keyword_plugin;
2089         PL_keyword_plugin = my_keyword_plugin;
2090 } WARNINGS_RESET