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