line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
/* vi: set ft=c : */ |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#include "EXTERN.h" |
4
|
|
|
|
|
|
|
#include "perl.h" |
5
|
|
|
|
|
|
|
#include "XSUB.h" |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
#define HAVE_PERL_VERSION(R, V, S) \ |
8
|
|
|
|
|
|
|
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S)))))) |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#include "XSParseSublike.h" |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
/* Skip this entire file on perls older than OP_ARGCHECK */ |
13
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5, 26, 0) |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
#define PERL_EXT |
16
|
|
|
|
|
|
|
/* We need to be able to see FEATURE_*_IS_ENABLED */ |
17
|
|
|
|
|
|
|
#include "feature.h" |
18
|
|
|
|
|
|
|
/* Also need KEY_sigvar */ |
19
|
|
|
|
|
|
|
#include "keywords.h" |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
#include "parse_subsignature_ex.h" |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
#include "lexer-additions.c.inc" |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
#include "LOGOP_ANY.c.inc" |
26
|
|
|
|
|
|
|
#include "croak_from_caller.c.inc" |
27
|
|
|
|
|
|
|
#include "make_argcheck_aux.c.inc" |
28
|
|
|
|
|
|
|
#include "newSV_with_free.c.inc" |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
#define newSVpvx(ptr) S_newSVpvx(aTHX_ ptr) |
31
|
|
|
|
|
|
|
static SV *S_newSVpvx(pTHX_ void *ptr) |
32
|
|
|
|
|
|
|
{ |
33
|
13
|
|
|
|
|
|
SV *sv = newSV(0); |
34
|
13
|
|
|
|
|
|
sv_upgrade(sv, SVt_PV); |
35
|
13
|
|
|
|
|
|
SvPVX(sv) = ptr; |
36
|
|
|
|
|
|
|
return sv; |
37
|
|
|
|
|
|
|
} |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
/* |
40
|
|
|
|
|
|
|
* Need to grab some things that aren't quite core perl API |
41
|
|
|
|
|
|
|
*/ |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
/* yyerror() is a long function and hard to emulate or copy-paste for our |
44
|
|
|
|
|
|
|
* purposes; we'll reïmplement a smaller version of it |
45
|
|
|
|
|
|
|
*/ |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
#define LEX_IGNORE_UTF8_HINTS 0x00000002 |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#define PL_linestr (PL_parser->linestr) |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
#ifdef USE_UTF8_SCRIPTS |
52
|
|
|
|
|
|
|
# define UTF cBOOL(!IN_BYTES) |
53
|
|
|
|
|
|
|
#else |
54
|
|
|
|
|
|
|
# define UTF cBOOL((PL_linestr && DO_UTF8(PL_linestr)) || ( !(PL_parser->lex_flags & LEX_IGNORE_UTF8_HINTS) && (PL_hints & HINT_UTF8))) |
55
|
|
|
|
|
|
|
#endif |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
#define yyerror(s) S_yyerror(aTHX_ s) |
58
|
0
|
|
|
|
|
|
void S_yyerror(pTHX_ const char *s) |
59
|
|
|
|
|
|
|
{ |
60
|
0
|
|
|
|
|
|
SV *message = sv_2mortal(newSVpvs_flags("", 0)); |
61
|
|
|
|
|
|
|
|
62
|
0
|
|
|
|
|
|
char *context = PL_parser->oldbufptr; |
63
|
0
|
|
|
|
|
|
STRLEN contlen = PL_parser->bufptr - PL_parser->oldbufptr; |
64
|
|
|
|
|
|
|
|
65
|
0
|
0
|
|
|
|
|
sv_catpvf(message, "%s at %s line %" IVdf, |
66
|
0
|
|
|
|
|
|
s, OutCopFILE(PL_curcop), (IV)CopLINE(PL_curcop)); |
67
|
|
|
|
|
|
|
|
68
|
0
|
0
|
|
|
|
|
if(context) |
69
|
0
|
0
|
|
|
|
|
sv_catpvf(message, ", near \"%" UTF8f "\"", |
|
|
0
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
|
UTF8fARG(UTF, contlen, context)); |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
71
|
|
|
|
|
|
|
|
72
|
0
|
|
|
|
|
|
sv_catpvf(message, "\n"); |
73
|
|
|
|
|
|
|
|
74
|
0
|
|
|
|
|
|
PL_parser->error_count++; |
75
|
0
|
|
|
|
|
|
warn_sv(message); |
76
|
0
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
/* Stolen from op.c */ |
79
|
|
|
|
|
|
|
#ifndef OpTYPE_set |
80
|
|
|
|
|
|
|
# define OpTYPE_set(op, type) \ |
81
|
|
|
|
|
|
|
STMT_START { \ |
82
|
|
|
|
|
|
|
op->op_type = (OPCODE)type; \ |
83
|
|
|
|
|
|
|
op->op_ppaddr = PL_ppaddr[type]; \ |
84
|
|
|
|
|
|
|
} STMT_END |
85
|
|
|
|
|
|
|
#endif |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
#define alloc_LOGOP(a,b,c) S_alloc_LOGOP(aTHX_ a,b,c) |
88
|
1
|
|
|
|
|
|
static LOGOP *S_alloc_LOGOP(pTHX_ I32 type, OP *first, OP* other) |
89
|
|
|
|
|
|
|
{ |
90
|
|
|
|
|
|
|
dVAR; |
91
|
|
|
|
|
|
|
LOGOP *logop; |
92
|
|
|
|
|
|
|
OP *kid = first; |
93
|
1
|
|
|
|
|
|
NewOp(1101, logop, 1, LOGOP); |
94
|
1
|
|
|
|
|
|
OpTYPE_set(logop, type); |
95
|
1
|
|
|
|
|
|
logop->op_first = first; |
96
|
1
|
|
|
|
|
|
logop->op_other = other; |
97
|
1
|
50
|
|
|
|
|
if (first) |
98
|
1
|
|
|
|
|
|
logop->op_flags = OPf_KIDS; |
99
|
1
|
50
|
|
|
|
|
while (kid && OpHAS_SIBLING(kid)) |
|
|
50
|
|
|
|
|
|
100
|
1
|
0
|
|
|
|
|
kid = OpSIBLING(kid); |
101
|
1
|
50
|
|
|
|
|
if (kid) |
102
|
1
|
|
|
|
|
|
OpLASTSIB_set(kid, (OP*)logop); |
103
|
1
|
|
|
|
|
|
return logop; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
/* copypaste from core's pp.c */ |
107
|
|
|
|
|
|
|
static SV * |
108
|
2
|
|
|
|
|
|
S_find_runcv_name(pTHX) |
109
|
|
|
|
|
|
|
{ |
110
|
|
|
|
|
|
|
CV *cv; |
111
|
|
|
|
|
|
|
GV *gv; |
112
|
|
|
|
|
|
|
SV *sv; |
113
|
|
|
|
|
|
|
|
114
|
2
|
|
|
|
|
|
cv = find_runcv(0); |
115
|
2
|
50
|
|
|
|
|
if (!cv) |
116
|
|
|
|
|
|
|
return &PL_sv_no; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
gv = CvGV(cv); |
119
|
2
|
50
|
|
|
|
|
if (!gv) |
120
|
|
|
|
|
|
|
return &PL_sv_no; |
121
|
|
|
|
|
|
|
|
122
|
2
|
|
|
|
|
|
sv = sv_newmortal(); |
123
|
2
|
|
|
|
|
|
gv_fullname4(sv, gv, NULL, TRUE); |
124
|
2
|
|
|
|
|
|
return sv; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
17
|
|
|
|
|
|
static OP *pp_namedargdefelem(pTHX) |
128
|
|
|
|
|
|
|
{ |
129
|
17
|
|
|
|
|
|
dSP; |
130
|
17
|
|
|
|
|
|
ANY *op_any = cLOGOP_ANY->op_any; |
131
|
17
|
|
|
|
|
|
SV *keysv = op_any[0].any_sv; |
132
|
17
|
|
|
|
|
|
HV *slurpy_hv = (HV *)PAD_SVl(op_any[1].any_iv); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
assert(slurpy_hv && SvTYPE(slurpy_hv) == SVt_PVHV); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
/* TODO: we could precompute the hash and store it in the ANY vector */ |
137
|
17
|
|
|
|
|
|
SV *value = hv_delete_ent(slurpy_hv, keysv, 0, 0); |
138
|
|
|
|
|
|
|
|
139
|
17
|
100
|
|
|
|
|
if(value) { |
140
|
14
|
50
|
|
|
|
|
EXTEND(SP, 1); |
141
|
14
|
|
|
|
|
|
PUSHs(value); |
142
|
14
|
|
|
|
|
|
RETURN; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
3
|
100
|
|
|
|
|
if(cLOGOP->op_other) |
146
|
|
|
|
|
|
|
return cLOGOP->op_other; |
147
|
|
|
|
|
|
|
|
148
|
1
|
|
|
|
|
|
croak_from_caller("Missing argument '%" SVf "' for subroutine %" SVf, |
149
|
|
|
|
|
|
|
SVfARG(keysv), SVfARG(S_find_runcv_name(aTHX))); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
8
|
|
|
|
|
|
static OP *pp_checknomorenamed(pTHX) |
153
|
|
|
|
|
|
|
{ |
154
|
8
|
|
|
|
|
|
HV *slurpy_hv = (HV *)PAD_SVl(PL_op->op_targ); |
155
|
|
|
|
|
|
|
|
156
|
8
|
100
|
|
|
|
|
if(!hv_iterinit(slurpy_hv)) |
157
|
7
|
|
|
|
|
|
return NORMAL; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
/* There are remaining named arguments; concat their names into a message */ |
160
|
|
|
|
|
|
|
|
161
|
1
|
|
|
|
|
|
HE *he = hv_iternext(slurpy_hv); |
162
|
|
|
|
|
|
|
|
163
|
1
|
|
|
|
|
|
SV *keynames = newSVpvn("", 0); |
164
|
1
|
|
|
|
|
|
SAVEFREESV(keynames); |
165
|
|
|
|
|
|
|
|
166
|
1
|
50
|
|
|
|
|
sv_catpvf(keynames, "'%" SVf "'", SVfARG(HeSVKEY_force(he))); |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
IV nkeys = 1; |
169
|
|
|
|
|
|
|
|
170
|
1
|
50
|
|
|
|
|
while((he = hv_iternext(slurpy_hv))) |
171
|
0
|
0
|
|
|
|
|
sv_catpvf(keynames, ", '%" SVf "'", SVfARG(HeSVKEY_force(he))), nkeys++; |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
1
|
50
|
|
|
|
|
croak_from_caller("Unrecognised %s %" SVf " for subroutine %" SVf, |
174
|
|
|
|
|
|
|
nkeys > 1 ? "arguments" : "argument", |
175
|
|
|
|
|
|
|
SVfARG(keynames), SVfARG(S_find_runcv_name(aTHX))); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
#define OP_IS_NAMED_PARAM(o) (o->op_type == OP_ARGELEM && cUNOPx(o)->op_first && \ |
179
|
|
|
|
|
|
|
cUNOPx(o)->op_first->op_type == OP_CUSTOM && \ |
180
|
|
|
|
|
|
|
cUNOPx(o)->op_first->op_ppaddr == &pp_namedargdefelem) |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
/* Parameter attribute extensions */ |
183
|
|
|
|
|
|
|
typedef struct SignatureAttributeRegistration SignatureAttributeRegistration; |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
struct SignatureAttributeRegistration { |
186
|
|
|
|
|
|
|
SignatureAttributeRegistration *next; |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
const char *name; |
189
|
|
|
|
|
|
|
STRLEN permit_hintkeylen; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
const struct XPSSignatureAttributeFuncs *funcs; |
192
|
|
|
|
|
|
|
void *funcdata; |
193
|
|
|
|
|
|
|
}; |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
static SignatureAttributeRegistration *sigattrs = NULL; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
#define find_registered_attribute(name) S_find_registered_attribute(aTHX_ name) |
198
|
4
|
|
|
|
|
|
static SignatureAttributeRegistration *S_find_registered_attribute(pTHX_ const char *name) |
199
|
|
|
|
|
|
|
{ |
200
|
4
|
|
|
|
|
|
HV *hints = GvHV(PL_hintgv); |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
SignatureAttributeRegistration *reg; |
203
|
4
|
50
|
|
|
|
|
for(reg = sigattrs; reg; reg = reg->next) { |
204
|
4
|
50
|
|
|
|
|
if(!strEQ(name, reg->name)) |
205
|
0
|
|
|
|
|
|
continue; |
206
|
|
|
|
|
|
|
|
207
|
4
|
50
|
|
|
|
|
if(reg->funcs->permit_hintkey && |
|
|
50
|
|
|
|
|
|
208
|
4
|
50
|
|
|
|
|
(!hints || !hv_fetch(hints, reg->funcs->permit_hintkey, reg->permit_hintkeylen, 0))) |
209
|
0
|
|
|
|
|
|
continue; |
210
|
|
|
|
|
|
|
|
211
|
4
|
|
|
|
|
|
return reg; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
|
croak("Unrecognised signature parameter attribute :%s", name); |
215
|
|
|
|
|
|
|
} |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
struct PendingSignatureFunc { |
218
|
|
|
|
|
|
|
const struct XPSSignatureAttributeFuncs *funcs; |
219
|
|
|
|
|
|
|
void *funcdata; |
220
|
|
|
|
|
|
|
void *attrdata; |
221
|
|
|
|
|
|
|
}; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
#define PENDING_FROM_SV(sv) ((struct PendingSignatureFunc *)SvPVX(sv)) |
224
|
|
|
|
|
|
|
|
225
|
0
|
|
|
|
|
|
static void pending_free(pTHX_ SV *sv) |
226
|
|
|
|
|
|
|
{ |
227
|
0
|
|
|
|
|
|
struct PendingSignatureFunc *p = PENDING_FROM_SV(sv); |
228
|
|
|
|
|
|
|
|
229
|
0
|
0
|
|
|
|
|
if(p->funcs->free) |
230
|
0
|
|
|
|
|
|
(*p->funcs->free)(aTHX_ p->attrdata, p->funcdata); |
231
|
0
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
#define NEW_SV_PENDING() newSV_with_free(sizeof(struct PendingSignatureFunc), &pending_free) |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
struct SignatureParsingContext { |
236
|
|
|
|
|
|
|
AV *named_varops; /* SV ptrs to the varop of every named parameter */ |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
OP *last_varop; /* the most recently-constructed varop */ |
239
|
|
|
|
|
|
|
}; |
240
|
|
|
|
|
|
|
|
241
|
22
|
|
|
|
|
|
static void free_parsing_ctx(pTHX_ void *_ctx) |
242
|
|
|
|
|
|
|
{ |
243
|
|
|
|
|
|
|
struct SignatureParsingContext *ctx = _ctx; |
244
|
22
|
100
|
|
|
|
|
if(ctx->named_varops) |
245
|
|
|
|
|
|
|
SvREFCNT_dec((SV *)ctx->named_varops); |
246
|
22
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
#define parse_sigelem(ctx, flags) S_parse_sigelem(aTHX_ ctx, flags) |
249
|
33
|
|
|
|
|
|
static OP *S_parse_sigelem(pTHX_ struct SignatureParsingContext *ctx, U32 flags) |
250
|
|
|
|
|
|
|
{ |
251
|
33
|
|
|
|
|
|
bool permit_attributes = flags & PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES; |
252
|
|
|
|
|
|
|
|
253
|
33
|
|
|
|
|
|
yy_parser *parser = PL_parser; |
254
|
|
|
|
|
|
|
|
255
|
33
|
|
|
|
|
|
int c = lex_peek_unichar(0); |
256
|
|
|
|
|
|
|
int private; |
257
|
33
|
|
|
|
|
|
struct XPSSignatureParamContext paramctx = {}; |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
AV *pending = NULL; |
260
|
|
|
|
|
|
|
|
261
|
33
|
100
|
|
|
|
|
if((flags & PARSE_SUBSIGNATURE_NAMED_PARAMS) && c == ':') { |
|
|
100
|
|
|
|
|
|
262
|
13
|
|
|
|
|
|
lex_read_unichar(0); |
263
|
13
|
|
|
|
|
|
lex_read_space(0); |
264
|
|
|
|
|
|
|
|
265
|
13
|
|
|
|
|
|
paramctx.is_named = true; |
266
|
13
|
|
|
|
|
|
c = lex_peek_unichar(0); |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
33
|
|
|
|
|
|
switch(c) { |
270
|
|
|
|
|
|
|
case '$': private = OPpARGELEM_SV; break; |
271
|
2
|
|
|
|
|
|
case '@': private = OPpARGELEM_AV; break; |
272
|
2
|
|
|
|
|
|
case '%': private = OPpARGELEM_HV; break; |
273
|
|
|
|
|
|
|
default: |
274
|
0
|
|
|
|
|
|
croak("Expected a signature element at <%s>\n", parser->bufptr); |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
33
|
|
|
|
|
|
char *lexname = parser->bufptr; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
/* Consume sigil */ |
280
|
33
|
|
|
|
|
|
lex_read_unichar(0); |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
char *lexname_end = NULL; |
283
|
|
|
|
|
|
|
|
284
|
33
|
50
|
|
|
|
|
if(isIDFIRST_uni(lex_peek_unichar(0))) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
285
|
32
|
|
|
|
|
|
lex_read_unichar(0); |
286
|
48
|
50
|
|
|
|
|
while(isALNUM_uni(lex_peek_unichar(0))) |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
287
|
16
|
|
|
|
|
|
lex_read_unichar(0); |
288
|
|
|
|
|
|
|
|
289
|
32
|
|
|
|
|
|
paramctx.varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (parser->sig_elems))); |
290
|
32
|
|
|
|
|
|
paramctx.varop->op_private |= private; |
291
|
|
|
|
|
|
|
|
292
|
32
|
100
|
|
|
|
|
if(paramctx.is_named) { |
293
|
13
|
100
|
|
|
|
|
if(!ctx->named_varops) |
294
|
9
|
|
|
|
|
|
ctx->named_varops = newAV(); |
295
|
|
|
|
|
|
|
|
296
|
13
|
|
|
|
|
|
av_push(ctx->named_varops, newSVpvx(paramctx.varop)); |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
32
|
|
|
|
|
|
ctx->last_varop = paramctx.varop; |
300
|
|
|
|
|
|
|
|
301
|
32
|
|
|
|
|
|
ENTER; |
302
|
32
|
|
|
|
|
|
SAVEI16(PL_parser->in_my); |
303
|
32
|
|
|
|
|
|
PL_parser->in_my = KEY_sigvar; |
304
|
|
|
|
|
|
|
|
305
|
32
|
|
|
|
|
|
lexname_end = PL_parser->bufptr; |
306
|
64
|
|
|
|
|
|
paramctx.padix = paramctx.varop->op_targ = |
307
|
32
|
|
|
|
|
|
pad_add_name_pvn(lexname, lexname_end - lexname, 0, NULL, NULL); |
308
|
|
|
|
|
|
|
|
309
|
32
|
|
|
|
|
|
LEAVE; |
310
|
|
|
|
|
|
|
|
311
|
32
|
|
|
|
|
|
lex_read_space(0); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
33
|
100
|
|
|
|
|
if(permit_attributes && lex_peek_unichar(0) == ':') { |
|
|
100
|
|
|
|
|
|
315
|
4
|
|
|
|
|
|
lex_read_unichar(0); |
316
|
4
|
|
|
|
|
|
lex_read_space(0); |
317
|
|
|
|
|
|
|
|
318
|
4
|
|
|
|
|
|
SV *attrname = sv_newmortal(), *attrval = sv_newmortal(); |
319
|
|
|
|
|
|
|
|
320
|
8
|
100
|
|
|
|
|
while(lex_scan_attrval_into(attrname, attrval)) { |
321
|
4
|
|
|
|
|
|
lex_read_space(0); |
322
|
|
|
|
|
|
|
|
323
|
4
|
50
|
|
|
|
|
SignatureAttributeRegistration *reg = find_registered_attribute(SvPV_nolen(attrname)); |
324
|
|
|
|
|
|
|
|
325
|
4
|
|
|
|
|
|
void *attrdata = NULL; |
326
|
4
|
50
|
|
|
|
|
if(reg->funcs->apply) |
327
|
4
|
|
|
|
|
|
(*reg->funcs->apply)(aTHX_ ¶mctx, attrval, &attrdata, reg->funcdata); |
328
|
|
|
|
|
|
|
|
329
|
4
|
50
|
|
|
|
|
if(attrdata || reg->funcs->post_defop) { |
|
|
50
|
|
|
|
|
|
330
|
0
|
0
|
|
|
|
|
if(!pending) { |
331
|
0
|
|
|
|
|
|
pending = newAV(); |
332
|
0
|
|
|
|
|
|
SAVEFREESV(pending); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
SV *psv; |
336
|
0
|
|
|
|
|
|
av_push(pending, psv = NEW_SV_PENDING()); |
337
|
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
|
PENDING_FROM_SV(psv)->funcs = reg->funcs; |
339
|
0
|
|
|
|
|
|
PENDING_FROM_SV(psv)->funcdata = reg->funcdata; |
340
|
0
|
|
|
|
|
|
PENDING_FROM_SV(psv)->attrdata = attrdata; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
4
|
50
|
|
|
|
|
if(lex_peek_unichar(0) == ':') { |
344
|
0
|
|
|
|
|
|
lex_read_unichar(0); |
345
|
4
|
|
|
|
|
|
lex_read_space(0); |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
|
350
|
33
|
100
|
|
|
|
|
if(c == '$') { |
351
|
|
|
|
|
|
|
SV *argname = NULL; |
352
|
|
|
|
|
|
|
|
353
|
29
|
100
|
|
|
|
|
if(paramctx.is_named) { |
354
|
13
|
|
|
|
|
|
parser->sig_slurpy = '+'; |
355
|
13
|
|
|
|
|
|
argname = newSVpvn(lexname + 1, lexname_end - lexname - 1); |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
else { |
358
|
16
|
50
|
|
|
|
|
if(parser->sig_slurpy) |
359
|
0
|
|
|
|
|
|
yyerror("Slurpy parameters not last"); |
360
|
|
|
|
|
|
|
|
361
|
16
|
|
|
|
|
|
parser->sig_elems++; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
29
|
100
|
|
|
|
|
if(lex_peek_unichar(0) == '=') { |
365
|
3
|
|
|
|
|
|
lex_read_unichar(0); |
366
|
3
|
|
|
|
|
|
lex_read_space(0); |
367
|
|
|
|
|
|
|
|
368
|
3
|
100
|
|
|
|
|
if(!paramctx.is_named) |
369
|
1
|
|
|
|
|
|
parser->sig_optelems++; |
370
|
|
|
|
|
|
|
|
371
|
3
|
|
|
|
|
|
OP *defexpr = parse_termexpr(0); |
372
|
|
|
|
|
|
|
|
373
|
3
|
100
|
|
|
|
|
if(paramctx.is_named) { |
374
|
2
|
50
|
|
|
|
|
paramctx.defop = (OP *)alloc_LOGOP_ANY(OP_CUSTOM, defexpr, LINKLIST(defexpr)); |
375
|
2
|
|
|
|
|
|
paramctx.defop->op_ppaddr = &pp_namedargdefelem; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
else { |
378
|
1
|
50
|
|
|
|
|
paramctx.defop = (OP *)alloc_LOGOP(OP_ARGDEFELEM, defexpr, LINKLIST(defexpr)); |
379
|
1
|
|
|
|
|
|
paramctx.defop->op_targ = (PADOFFSET)(parser->sig_elems - 1); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
3
|
|
|
|
|
|
paramctx.varop->op_flags |= OPf_STACKED; |
383
|
3
|
|
|
|
|
|
op_sibling_splice(paramctx.varop, NULL, 0, paramctx.defop); |
384
|
3
|
|
|
|
|
|
paramctx.defop = op_contextualize(paramctx.defop, G_SCALAR); |
385
|
|
|
|
|
|
|
|
386
|
3
|
50
|
|
|
|
|
LINKLIST(paramctx.varop); |
387
|
|
|
|
|
|
|
|
388
|
3
|
|
|
|
|
|
paramctx.varop->op_next = paramctx.defop; |
389
|
3
|
|
|
|
|
|
defexpr->op_next = paramctx.varop; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
else { |
392
|
26
|
50
|
|
|
|
|
if(parser->sig_optelems) |
393
|
0
|
|
|
|
|
|
yyerror("Mandatory parameter follows optional parameter"); |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
|
396
|
29
|
100
|
|
|
|
|
if(paramctx.is_named) { |
397
|
13
|
|
|
|
|
|
OP *defop = paramctx.defop; |
398
|
13
|
100
|
|
|
|
|
if(!defop) { |
399
|
11
|
|
|
|
|
|
defop = (OP *)alloc_LOGOP_ANY(OP_CUSTOM, NULL, NULL); |
400
|
11
|
|
|
|
|
|
defop->op_ppaddr = &pp_namedargdefelem; |
401
|
|
|
|
|
|
|
|
402
|
11
|
|
|
|
|
|
paramctx.varop->op_flags |= OPf_STACKED; |
403
|
11
|
|
|
|
|
|
op_sibling_splice(paramctx.varop, NULL, 0, defop); |
404
|
|
|
|
|
|
|
|
405
|
11
|
50
|
|
|
|
|
LINKLIST(paramctx.varop); |
406
|
|
|
|
|
|
|
|
407
|
11
|
|
|
|
|
|
paramctx.varop->op_next = defop; |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
ANY *op_any; |
411
|
13
|
|
|
|
|
|
Newx(op_any, 2, ANY); |
412
|
|
|
|
|
|
|
|
413
|
13
|
|
|
|
|
|
op_any[0].any_sv = argname; |
414
|
|
|
|
|
|
|
/* [1] is filled in later */ |
415
|
|
|
|
|
|
|
|
416
|
13
|
|
|
|
|
|
cLOGOP_ANYx(defop)->op_any = op_any; |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
else { |
420
|
4
|
50
|
|
|
|
|
if(paramctx.is_named) |
421
|
0
|
|
|
|
|
|
yyerror("Slurpy parameters may not be named"); |
422
|
4
|
50
|
|
|
|
|
if(parser->sig_slurpy && parser->sig_slurpy != '+') |
423
|
0
|
|
|
|
|
|
yyerror("Multiple slurpy parameters not allowed"); |
424
|
|
|
|
|
|
|
|
425
|
4
|
|
|
|
|
|
parser->sig_slurpy = c; |
426
|
|
|
|
|
|
|
|
427
|
4
|
50
|
|
|
|
|
if(lex_peek_unichar(0) == '=') |
428
|
0
|
|
|
|
|
|
yyerror("A slurpy parameter may not have a default value"); |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
33
|
|
|
|
|
|
paramctx.op = paramctx.varop; |
432
|
|
|
|
|
|
|
|
433
|
33
|
50
|
|
|
|
|
if(pending) { |
434
|
0
|
0
|
|
|
|
|
for(int i = 0; i <= AvFILL(pending); i++) { |
|
|
0
|
|
|
|
|
|
435
|
0
|
|
|
|
|
|
struct PendingSignatureFunc *p = PENDING_FROM_SV(AvARRAY(pending)[i]); |
436
|
|
|
|
|
|
|
|
437
|
0
|
0
|
|
|
|
|
if(p->funcs->post_defop) |
438
|
0
|
|
|
|
|
|
(*p->funcs->post_defop)(aTHX_ ¶mctx, p->attrdata, p->funcdata); |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
33
|
100
|
|
|
|
|
return paramctx.op ? newSTATEOP(0, NULL, paramctx.op) : NULL; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
22
|
|
|
|
|
|
OP *XPS_parse_subsignature_ex(pTHX_ int flags) |
446
|
|
|
|
|
|
|
{ |
447
|
|
|
|
|
|
|
/* Mostly reconstructed logic from perl 5.28.0's toke.c and perly.y |
448
|
|
|
|
|
|
|
*/ |
449
|
22
|
|
|
|
|
|
yy_parser *parser = PL_parser; |
450
|
22
|
|
|
|
|
|
struct SignatureParsingContext ctx = {}; |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
assert((flags & ~(PARSE_SUBSIGNATURE_NAMED_PARAMS|PARSE_SUBSIGNATURE_PARAM_ATTRIBUTES)) == 0); |
453
|
|
|
|
|
|
|
|
454
|
22
|
|
|
|
|
|
ENTER; |
455
|
22
|
|
|
|
|
|
SAVEDESTRUCTOR_X(&free_parsing_ctx, &ctx); |
456
|
|
|
|
|
|
|
|
457
|
22
|
|
|
|
|
|
SAVEIV(parser->sig_elems); |
458
|
22
|
|
|
|
|
|
SAVEIV(parser->sig_optelems); |
459
|
22
|
|
|
|
|
|
SAVEI8(parser->sig_slurpy); |
460
|
|
|
|
|
|
|
|
461
|
22
|
|
|
|
|
|
parser->sig_elems = 0; |
462
|
22
|
|
|
|
|
|
parser->sig_optelems = 0; |
463
|
22
|
|
|
|
|
|
parser->sig_slurpy = 0; |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
OP *elems = NULL; |
466
|
|
|
|
|
|
|
OP *namedelems = NULL; |
467
|
|
|
|
|
|
|
OP *final_elem = NULL; |
468
|
|
|
|
|
|
|
|
469
|
36
|
100
|
|
|
|
|
while(lex_peek_unichar(0) != ')') { |
470
|
33
|
|
|
|
|
|
lex_read_space(0); |
471
|
33
|
|
|
|
|
|
OP *elem = parse_sigelem(&ctx, flags); |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
/* placeholder anonymous elems are NULL */ |
474
|
33
|
100
|
|
|
|
|
if(elem) { |
475
|
|
|
|
|
|
|
/* elem should be an OP_LINESEQ[ OP_NEXTSTATE. actual elem ] */ |
476
|
|
|
|
|
|
|
assert(elem->op_type == OP_LINESEQ); |
477
|
|
|
|
|
|
|
assert(cLISTOPx(elem)->op_first); |
478
|
|
|
|
|
|
|
assert(OpSIBLING(cLISTOPx(elem)->op_first)); |
479
|
|
|
|
|
|
|
|
480
|
32
|
50
|
|
|
|
|
final_elem = OpSIBLING(cLISTOPx(elem)->op_first); |
481
|
|
|
|
|
|
|
|
482
|
32
|
50
|
|
|
|
|
if(OP_IS_NAMED_PARAM(ctx.last_varop)) |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
483
|
13
|
|
|
|
|
|
namedelems = op_append_list(OP_LIST, namedelems, elem); |
484
|
|
|
|
|
|
|
else |
485
|
19
|
|
|
|
|
|
elems = op_append_list(OP_LINESEQ, elems, elem); |
486
|
|
|
|
|
|
|
} |
487
|
|
|
|
|
|
|
|
488
|
33
|
50
|
|
|
|
|
if(PL_parser->error_count) { |
489
|
0
|
|
|
|
|
|
LEAVE; |
490
|
0
|
|
|
|
|
|
return NULL; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
33
|
|
|
|
|
|
lex_read_space(0); |
494
|
33
|
|
|
|
|
|
switch(lex_peek_unichar(0)) { |
495
|
|
|
|
|
|
|
case ')': goto endofelems; |
496
|
|
|
|
|
|
|
case ',': break; |
497
|
|
|
|
|
|
|
default: |
498
|
0
|
|
|
|
|
|
fprintf(stderr, "ARGH unsure how to proceed parse_subsignature at <%s>\n", |
499
|
|
|
|
|
|
|
parser->bufptr); |
500
|
0
|
|
|
|
|
|
croak("ARGH"); |
501
|
|
|
|
|
|
|
break; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
14
|
|
|
|
|
|
lex_read_unichar(0); |
505
|
14
|
|
|
|
|
|
lex_read_space(0); |
506
|
|
|
|
|
|
|
} |
507
|
|
|
|
|
|
|
endofelems: |
508
|
|
|
|
|
|
|
|
509
|
22
|
50
|
|
|
|
|
if (!FEATURE_SIGNATURES_IS_ENABLED) |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
510
|
0
|
|
|
|
|
|
croak("Experimental subroutine signatures not enabled"); |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
#if !HAVE_PERL_VERSION(5, 37, 0) |
513
|
22
|
|
|
|
|
|
Perl_ck_warner_d(aTHX_ packWARN(WARN_EXPERIMENTAL__SIGNATURES), |
514
|
|
|
|
|
|
|
"The signatures feature is experimental"); |
515
|
|
|
|
|
|
|
#endif |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
bool allow_extras_after_named = true; |
518
|
22
|
100
|
|
|
|
|
if(ctx.named_varops) { |
519
|
9
|
|
|
|
|
|
switch(PL_parser->sig_slurpy) { |
520
|
|
|
|
|
|
|
case 0: |
521
|
|
|
|
|
|
|
case '@': |
522
|
0
|
|
|
|
|
|
NOT_REACHED; |
523
|
|
|
|
|
|
|
case '+': |
524
|
|
|
|
|
|
|
{ |
525
|
|
|
|
|
|
|
/* Pretend we have a new, unnamed slurpy hash */ |
526
|
7
|
|
|
|
|
|
OP *varop = newUNOP_AUX(OP_ARGELEM, 0, NULL, INT2PTR(UNOP_AUX_item *, (parser->sig_elems))); |
527
|
7
|
|
|
|
|
|
varop->op_private |= OPpARGELEM_HV; |
528
|
7
|
|
|
|
|
|
varop->op_targ = pad_add_name_pvs("%(params)", 0, NULL, NULL); |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
final_elem = varop; |
531
|
|
|
|
|
|
|
|
532
|
7
|
|
|
|
|
|
OP *elem = newSTATEOP(0, NULL, varop); |
533
|
7
|
|
|
|
|
|
elems = op_append_list(OP_LINESEQ, elems, elem); |
534
|
|
|
|
|
|
|
|
535
|
7
|
|
|
|
|
|
PL_parser->sig_slurpy = '%'; |
536
|
|
|
|
|
|
|
allow_extras_after_named = false; |
537
|
|
|
|
|
|
|
} |
538
|
7
|
|
|
|
|
|
break; |
539
|
|
|
|
|
|
|
case '%': |
540
|
|
|
|
|
|
|
break; |
541
|
|
|
|
|
|
|
} |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
22
|
|
|
|
|
|
UNOP_AUX_item *aux = make_argcheck_aux( |
545
|
|
|
|
|
|
|
parser->sig_elems, parser->sig_optelems, parser->sig_slurpy); |
546
|
|
|
|
|
|
|
|
547
|
22
|
|
|
|
|
|
OP *checkop = newUNOP_AUX(OP_ARGCHECK, 0, NULL, aux); |
548
|
|
|
|
|
|
|
|
549
|
22
|
|
|
|
|
|
OP *ops = op_prepend_elem(OP_LINESEQ, newSTATEOP(0, NULL, NULL), |
550
|
|
|
|
|
|
|
op_prepend_elem(OP_LINESEQ, checkop, elems)); |
551
|
|
|
|
|
|
|
|
552
|
22
|
100
|
|
|
|
|
if(ctx.named_varops) { |
553
|
|
|
|
|
|
|
assert(final_elem->op_type == OP_ARGELEM); |
554
|
|
|
|
|
|
|
assert(final_elem->op_private == OPpARGELEM_HV); |
555
|
|
|
|
|
|
|
|
556
|
9
|
|
|
|
|
|
PADOFFSET slurpy_padix = final_elem->op_targ; |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
/* Tell all the pp_namedargdefelem()s where to find the slurpy hash */ |
559
|
22
|
50
|
|
|
|
|
for(int i = 0; i <= AvFILL(ctx.named_varops); i++) { |
|
|
100
|
|
|
|
|
|
560
|
13
|
|
|
|
|
|
OP *elemop = (OP *)(SvPVX(AvARRAY(ctx.named_varops)[i])); |
561
|
|
|
|
|
|
|
assert(elemop); |
562
|
|
|
|
|
|
|
assert(OP_IS_NAMED_PARAM(elemop)); |
563
|
|
|
|
|
|
|
|
564
|
13
|
|
|
|
|
|
OP *defelemop = cUNOPx(elemop)->op_first; |
565
|
|
|
|
|
|
|
assert(defelemop); |
566
|
|
|
|
|
|
|
assert(defelemop->op_type == OP_CUSTOM && |
567
|
|
|
|
|
|
|
defelemop->op_ppaddr == &pp_namedargdefelem); |
568
|
13
|
|
|
|
|
|
ANY *op_any = cLOGOP_ANYx(defelemop)->op_any; |
569
|
13
|
|
|
|
|
|
op_any[1].any_iv = slurpy_padix; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
9
|
|
|
|
|
|
ops = op_append_list(OP_LINESEQ, ops, |
573
|
|
|
|
|
|
|
namedelems); |
574
|
|
|
|
|
|
|
|
575
|
9
|
100
|
|
|
|
|
if(!allow_extras_after_named) { |
576
|
7
|
|
|
|
|
|
ops = op_append_list(OP_LINESEQ, ops, |
577
|
|
|
|
|
|
|
newSTATEOP(0, NULL, checkop = newOP(OP_CUSTOM, 0))); |
578
|
7
|
|
|
|
|
|
checkop->op_ppaddr = &pp_checknomorenamed; |
579
|
7
|
|
|
|
|
|
checkop->op_targ = slurpy_padix; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
/* a nextstate at the end handles context correctly for an empty |
584
|
|
|
|
|
|
|
* sub body */ |
585
|
22
|
|
|
|
|
|
ops = op_append_elem(OP_LINESEQ, ops, newSTATEOP(0, NULL, NULL)); |
586
|
|
|
|
|
|
|
|
587
|
22
|
|
|
|
|
|
LEAVE; |
588
|
|
|
|
|
|
|
|
589
|
22
|
|
|
|
|
|
return ops; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
6
|
|
|
|
|
|
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata) |
593
|
|
|
|
|
|
|
{ |
594
|
|
|
|
|
|
|
SignatureAttributeRegistration *reg; |
595
|
6
|
|
|
|
|
|
Newx(reg, 1, struct SignatureAttributeRegistration); |
596
|
|
|
|
|
|
|
|
597
|
6
|
|
|
|
|
|
*reg = (struct SignatureAttributeRegistration){ |
598
|
|
|
|
|
|
|
.name = name, |
599
|
|
|
|
|
|
|
.funcs = funcs, |
600
|
|
|
|
|
|
|
.funcdata = funcdata, |
601
|
|
|
|
|
|
|
}; |
602
|
|
|
|
|
|
|
|
603
|
6
|
50
|
|
|
|
|
if(funcs->permit_hintkey) |
604
|
6
|
|
|
|
|
|
reg->permit_hintkeylen = strlen(funcs->permit_hintkey); |
605
|
|
|
|
|
|
|
|
606
|
6
|
|
|
|
|
|
reg->next = sigattrs; |
607
|
6
|
|
|
|
|
|
sigattrs = reg; |
608
|
6
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
#else /* !HAVE_PERL_VERSION(5, 26, 0) */ |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
void XPS_register_subsignature_attribute(pTHX_ const char *name, const struct XPSSignatureAttributeFuncs *funcs, void *funcdata) |
613
|
|
|
|
|
|
|
{ |
614
|
|
|
|
|
|
|
croak("Custom subroutine signature attributes are not supported on this verison of Perl"); |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
#endif |