line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#define PERL_NO_GET_CONTEXT |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
#include "EXTERN.h" |
4
|
|
|
|
|
|
|
#include "perl.h" |
5
|
|
|
|
|
|
|
#include "XSUB.h" |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
#include "XSParseKeyword.h" |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
#include "XSParseSublike.h" |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
#include "perl-backcompat.c.inc" |
12
|
|
|
|
|
|
|
#include "sv_setrv.c.inc" |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
#include "perl-additions.c.inc" |
15
|
|
|
|
|
|
|
#include "lexer-additions.c.inc" |
16
|
|
|
|
|
|
|
#include "forbid_outofblock_ops.c.inc" |
17
|
|
|
|
|
|
|
#include "force_list_keeping_pushmark.c.inc" |
18
|
|
|
|
|
|
|
#include "optree-additions.c.inc" |
19
|
|
|
|
|
|
|
#include "newOP_CUSTOM.c.inc" |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
#include "class_plain_parser.h" |
22
|
|
|
|
|
|
|
#include "class_plain_class.h" |
23
|
|
|
|
|
|
|
#include "class_plain_field.h" |
24
|
|
|
|
|
|
|
#include "class_plain_method.h" |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
/********************************** |
27
|
|
|
|
|
|
|
* Class and Field Implementation * |
28
|
|
|
|
|
|
|
**********************************/ |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
static XOP xop_methstart; |
31
|
47
|
|
|
|
|
|
static OP *pp_methstart(pTHX) |
32
|
|
|
|
|
|
|
{ |
33
|
47
|
|
|
|
|
|
SV *self = av_shift(GvAV(PL_defgv)); |
34
|
|
|
|
|
|
|
|
35
|
47
|
100
|
|
|
|
|
if(!SvROK(self) || !SvOBJECT(SvRV(self))) |
|
|
50
|
|
|
|
|
|
36
|
1
|
|
|
|
|
|
croak("Cannot invoke method on a non-instance"); |
37
|
|
|
|
|
|
|
|
38
|
46
|
|
|
|
|
|
save_clearsv(&PAD_SVl(1)); |
39
|
46
|
|
|
|
|
|
sv_setsv(PAD_SVl(1), self); |
40
|
|
|
|
|
|
|
|
41
|
46
|
|
|
|
|
|
return PL_op->op_next; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
43
|
|
|
|
|
|
OP *ClassPlain_newMETHSTARTOP(pTHX_ U32 flags) |
45
|
|
|
|
|
|
|
{ |
46
|
|
|
|
|
|
|
OP *op = newOP_CUSTOM(&pp_methstart, flags); |
47
|
43
|
|
|
|
|
|
op->op_private = (U8)(flags >> 8); |
48
|
43
|
|
|
|
|
|
return op; |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
static XOP xop_commonmethstart; |
52
|
21
|
|
|
|
|
|
static OP *pp_commonmethstart(pTHX) |
53
|
|
|
|
|
|
|
{ |
54
|
21
|
|
|
|
|
|
SV *self = av_shift(GvAV(PL_defgv)); |
55
|
|
|
|
|
|
|
|
56
|
21
|
50
|
|
|
|
|
if(SvROK(self)) |
57
|
|
|
|
|
|
|
/* TODO: Should handle this somehow */ |
58
|
0
|
|
|
|
|
|
croak("Cannot invoke common method on an instance"); |
59
|
|
|
|
|
|
|
|
60
|
21
|
|
|
|
|
|
save_clearsv(&PAD_SVl(1)); |
61
|
21
|
|
|
|
|
|
sv_setsv(PAD_SVl(1), self); |
62
|
|
|
|
|
|
|
|
63
|
21
|
|
|
|
|
|
return PL_op->op_next; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
18
|
|
|
|
|
|
OP *ClassPlain_newCOMMONMETHSTARTOP(pTHX_ U32 flags) |
67
|
|
|
|
|
|
|
{ |
68
|
|
|
|
|
|
|
OP *op = newOP_CUSTOM(&pp_commonmethstart, flags); |
69
|
18
|
|
|
|
|
|
op->op_private = (U8)(flags >> 8); |
70
|
18
|
|
|
|
|
|
return op; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
/* The metadata on the currently-compiling class */ |
74
|
|
|
|
|
|
|
#define compclass_meta S_compclass_meta(aTHX) |
75
|
199
|
|
|
|
|
|
static ClassMeta *S_compclass_meta(pTHX) |
76
|
|
|
|
|
|
|
{ |
77
|
199
|
|
|
|
|
|
SV **svp = hv_fetchs(GvHV(PL_hintgv), "Class::Plain/compclass_meta", 0); |
78
|
199
|
50
|
|
|
|
|
if(!svp || !*svp || !SvOK(*svp)) |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
79
|
|
|
|
|
|
|
return NULL; |
80
|
199
|
50
|
|
|
|
|
return (ClassMeta *)(intptr_t)SvIV(*svp); |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
#define have_compclass_meta S_have_compclass_meta(aTHX) |
84
|
106
|
|
|
|
|
|
static bool S_have_compclass_meta(pTHX) |
85
|
|
|
|
|
|
|
{ |
86
|
106
|
|
|
|
|
|
SV **svp = hv_fetchs(GvHV(PL_hintgv), "Class::Plain/compclass_meta", 0); |
87
|
106
|
100
|
|
|
|
|
if(!svp || !*svp) |
|
|
50
|
|
|
|
|
|
88
|
|
|
|
|
|
|
return false; |
89
|
|
|
|
|
|
|
|
90
|
103
|
50
|
|
|
|
|
if(SvOK(*svp) && SvIV(*svp)) |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
91
|
|
|
|
|
|
|
return true; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
return false; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
#define compclass_meta_set(meta) S_compclass_meta_set(aTHX_ meta) |
97
|
39
|
|
|
|
|
|
static void S_compclass_meta_set(pTHX_ ClassMeta *meta) |
98
|
|
|
|
|
|
|
{ |
99
|
39
|
|
|
|
|
|
SV *sv = *hv_fetchs(GvHV(PL_hintgv), "Class::Plain/compclass_meta", GV_ADD); |
100
|
39
|
|
|
|
|
|
sv_setiv(sv, (IV)(intptr_t)meta); |
101
|
39
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
#define is_valid_ident_utf8(s) S_is_valid_ident_utf8(aTHX_ s) |
104
|
|
|
|
|
|
|
static bool S_is_valid_ident_utf8(pTHX_ const U8 *s) |
105
|
|
|
|
|
|
|
{ |
106
|
|
|
|
|
|
|
const U8 *e = s + strlen((char *)s); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
if(!isIDFIRST_utf8_safe(s, e)) |
109
|
|
|
|
|
|
|
return false; |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
s += UTF8SKIP(s); |
112
|
|
|
|
|
|
|
while(*s) { |
113
|
|
|
|
|
|
|
if(!isIDCONT_utf8_safe(s, e)) |
114
|
|
|
|
|
|
|
return false; |
115
|
|
|
|
|
|
|
s += UTF8SKIP(s); |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
return true; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
37
|
|
|
|
|
|
static void inplace_trim_whitespace(SV *sv) |
122
|
|
|
|
|
|
|
{ |
123
|
37
|
100
|
|
|
|
|
if(!SvPOK(sv) || !SvCUR(sv)) |
|
|
100
|
|
|
|
|
|
124
|
|
|
|
|
|
|
return; |
125
|
|
|
|
|
|
|
|
126
|
12
|
|
|
|
|
|
char *dst = SvPVX(sv); |
127
|
|
|
|
|
|
|
char *src = dst; |
128
|
|
|
|
|
|
|
|
129
|
12
|
50
|
|
|
|
|
while(*src && isSPACE(*src)) |
|
|
50
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
src++; |
131
|
|
|
|
|
|
|
|
132
|
12
|
50
|
|
|
|
|
if(src > dst) { |
133
|
0
|
|
|
|
|
|
size_t offset = src - dst; |
134
|
0
|
|
|
|
|
|
Move(src, dst, SvCUR(sv) - offset, char); |
135
|
0
|
|
|
|
|
|
SvCUR(sv) -= offset; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
12
|
|
|
|
|
|
src = dst + SvCUR(sv) - 1; |
139
|
12
|
50
|
|
|
|
|
while(src > dst && isSPACE(*src)) |
|
|
50
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
src--; |
141
|
|
|
|
|
|
|
|
142
|
12
|
|
|
|
|
|
SvCUR(sv) = src - dst + 1; |
143
|
12
|
|
|
|
|
|
dst[SvCUR(sv)] = 0; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
18
|
|
|
|
|
|
static void S_apply_method_common(pTHX_ MethodMeta *meta, const char *val, void *_data) |
147
|
|
|
|
|
|
|
{ |
148
|
18
|
|
|
|
|
|
meta->is_common = true; |
149
|
18
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
static struct MethodAttributeDefinition method_attributes[] = { |
152
|
|
|
|
|
|
|
{ "common", &S_apply_method_common, NULL }, |
153
|
|
|
|
|
|
|
{ 0 } |
154
|
|
|
|
|
|
|
}; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
/******************* |
157
|
|
|
|
|
|
|
* Custom Keywords * |
158
|
|
|
|
|
|
|
*******************/ |
159
|
|
|
|
|
|
|
|
160
|
39
|
|
|
|
|
|
static int build_classlike(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata) |
161
|
|
|
|
|
|
|
{ |
162
|
|
|
|
|
|
|
int argi = 0; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
|
165
|
39
|
|
|
|
|
|
SV *packagename = args[argi++]->sv; |
166
|
|
|
|
|
|
|
/* Grrr; XPK bug */ |
167
|
39
|
50
|
|
|
|
|
if(!packagename) |
168
|
0
|
|
|
|
|
|
croak("Expected a class name after 'class'"); |
169
|
|
|
|
|
|
|
|
170
|
39
|
|
|
|
|
|
IV type = (IV)(intptr_t)hookdata; |
171
|
|
|
|
|
|
|
|
172
|
39
|
|
|
|
|
|
ClassMeta *meta = ClassPlain_create_class(aTHX_ type, packagename); |
173
|
|
|
|
|
|
|
|
174
|
39
|
|
|
|
|
|
int nattrs = args[argi++]->i; |
175
|
39
|
100
|
|
|
|
|
if(nattrs) { |
176
|
|
|
|
|
|
|
int i; |
177
|
21
|
100
|
|
|
|
|
for(i = 0; i < nattrs; i++) { |
178
|
11
|
|
|
|
|
|
SV *attrname = args[argi]->attr.name; |
179
|
11
|
|
|
|
|
|
SV *attrval = args[argi]->attr.value; |
180
|
|
|
|
|
|
|
|
181
|
11
|
|
|
|
|
|
inplace_trim_whitespace(attrval); |
182
|
|
|
|
|
|
|
|
183
|
11
|
|
|
|
|
|
ClassPlain_class_apply_attribute(aTHX_ meta, SvPVX(attrname), attrval); |
184
|
|
|
|
|
|
|
|
185
|
11
|
|
|
|
|
|
argi++; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
39
|
|
|
|
|
|
ClassPlain_begin_class_block(aTHX_ meta); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
/* At this point XS::Parse::Keyword has parsed all it can. From here we will |
192
|
|
|
|
|
|
|
* take over to perform the odd "block or statement" behaviour of `class` |
193
|
|
|
|
|
|
|
* keywords |
194
|
|
|
|
|
|
|
*/ |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
bool is_block; |
197
|
|
|
|
|
|
|
|
198
|
39
|
50
|
|
|
|
|
if(lex_consume_unichar('{')) { |
199
|
|
|
|
|
|
|
is_block = true; |
200
|
39
|
|
|
|
|
|
ENTER; |
201
|
|
|
|
|
|
|
} |
202
|
0
|
0
|
|
|
|
|
else if(lex_consume_unichar(';')) { |
203
|
|
|
|
|
|
|
is_block = false; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
else |
206
|
0
|
|
|
|
|
|
croak("Expected a block or ';'"); |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
/* CARGOCULT from perl/op.c:Perl_package() */ |
209
|
|
|
|
|
|
|
{ |
210
|
39
|
|
|
|
|
|
SAVEGENERICSV(PL_curstash); |
211
|
39
|
|
|
|
|
|
save_item(PL_curstname); |
212
|
|
|
|
|
|
|
|
213
|
39
|
|
|
|
|
|
PL_curstash = (HV *)SvREFCNT_inc(gv_stashsv(meta->name, GV_ADD)); |
214
|
39
|
|
|
|
|
|
sv_setsv(PL_curstname, packagename); |
215
|
|
|
|
|
|
|
|
216
|
39
|
|
|
|
|
|
PL_hints |= HINT_BLOCK_SCOPE; |
217
|
39
|
|
|
|
|
|
PL_parser->copline = NOLINE; |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
39
|
50
|
|
|
|
|
if(is_block) { |
221
|
39
|
|
|
|
|
|
I32 save_ix = block_start(TRUE); |
222
|
39
|
|
|
|
|
|
compclass_meta_set(meta); |
223
|
|
|
|
|
|
|
|
224
|
39
|
|
|
|
|
|
OP *body = parse_stmtseq(0); |
225
|
39
|
|
|
|
|
|
body = block_end(save_ix, body); |
226
|
|
|
|
|
|
|
|
227
|
39
|
50
|
|
|
|
|
if(!lex_consume_unichar('}')) |
228
|
0
|
|
|
|
|
|
croak("Expected }"); |
229
|
|
|
|
|
|
|
|
230
|
39
|
|
|
|
|
|
LEAVE; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
/* CARGOCULT from perl/perly.y:PACKAGE BAREWORD BAREWORD '{' */ |
233
|
|
|
|
|
|
|
/* a block is a loop that happens once */ |
234
|
39
|
|
|
|
|
|
*out = op_append_elem(OP_LINESEQ, |
235
|
|
|
|
|
|
|
newWHILEOP(0, 1, NULL, NULL, body, NULL, 0), |
236
|
|
|
|
|
|
|
newSVOP(OP_CONST, 0, &PL_sv_yes)); |
237
|
39
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
else { |
240
|
0
|
|
|
|
|
|
SAVEHINTS(); |
241
|
0
|
|
|
|
|
|
compclass_meta_set(meta); |
242
|
|
|
|
|
|
|
|
243
|
0
|
|
|
|
|
|
*out = newSVOP(OP_CONST, 0, &PL_sv_yes); |
244
|
0
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
static const struct XSParseKeywordPieceType pieces_classlike[] = { |
249
|
|
|
|
|
|
|
XPK_PACKAGENAME, |
250
|
|
|
|
|
|
|
/* This should really a repeated (tagged?) choice of a number of things, but |
251
|
|
|
|
|
|
|
* right now there's only one thing permitted here anyway |
252
|
|
|
|
|
|
|
*/ |
253
|
|
|
|
|
|
|
XPK_ATTRIBUTES, |
254
|
|
|
|
|
|
|
{0} |
255
|
|
|
|
|
|
|
}; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
static const struct XSParseKeywordHooks kwhooks_class = { |
258
|
|
|
|
|
|
|
.permit_hintkey = "Class::Plain/class", |
259
|
|
|
|
|
|
|
.pieces = pieces_classlike, |
260
|
|
|
|
|
|
|
.build = &build_classlike, |
261
|
|
|
|
|
|
|
}; |
262
|
|
|
|
|
|
|
|
263
|
40
|
|
|
|
|
|
static void check_field(pTHX_ void *hookdata) |
264
|
|
|
|
|
|
|
{ |
265
|
|
|
|
|
|
|
char *kwname = hookdata; |
266
|
|
|
|
|
|
|
|
267
|
40
|
100
|
|
|
|
|
if(!have_compclass_meta) |
268
|
2
|
|
|
|
|
|
croak("Cannot '%s' outside of 'class'", kwname); |
269
|
|
|
|
|
|
|
|
270
|
38
|
50
|
|
|
|
|
if(!sv_eq(PL_curstname, compclass_meta->name)) |
271
|
0
|
|
|
|
|
|
croak("Current package name no longer matches current class (%" SVf " vs %" SVf ")", |
272
|
0
|
|
|
|
|
|
PL_curstname, compclass_meta->name); |
273
|
38
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
38
|
|
|
|
|
|
static int build_field(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata) |
276
|
|
|
|
|
|
|
{ |
277
|
|
|
|
|
|
|
int argi = 0; |
278
|
|
|
|
|
|
|
|
279
|
38
|
|
|
|
|
|
SV *name = args[argi++]->sv; |
280
|
|
|
|
|
|
|
|
281
|
38
|
|
|
|
|
|
FieldMeta *fieldmeta = ClassPlain_class_add_field(aTHX_ compclass_meta, name); |
282
|
|
|
|
|
|
|
SvREFCNT_dec(name); |
283
|
|
|
|
|
|
|
|
284
|
38
|
|
|
|
|
|
int nattrs = args[argi++]->i; |
285
|
38
|
100
|
|
|
|
|
if(nattrs) { |
286
|
50
|
100
|
|
|
|
|
while(argi < (nattrs+2)) { |
287
|
26
|
|
|
|
|
|
SV *attrname = args[argi]->attr.name; |
288
|
26
|
|
|
|
|
|
SV *attrval = args[argi]->attr.value; |
289
|
|
|
|
|
|
|
|
290
|
26
|
|
|
|
|
|
inplace_trim_whitespace(attrval); |
291
|
|
|
|
|
|
|
|
292
|
26
|
|
|
|
|
|
ClassPlain_field_apply_attribute(aTHX_ fieldmeta, SvPVX(attrname), attrval); |
293
|
|
|
|
|
|
|
|
294
|
26
|
50
|
|
|
|
|
if(attrval) |
295
|
|
|
|
|
|
|
SvREFCNT_dec(attrval); |
296
|
|
|
|
|
|
|
|
297
|
26
|
|
|
|
|
|
argi++; |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
38
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
static const struct XSParseKeywordHooks kwhooks_field = { |
305
|
|
|
|
|
|
|
.flags = XPK_FLAG_STMT, |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
.check = &check_field, |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
.permit_hintkey = "Class::Plain/field", |
310
|
|
|
|
|
|
|
.pieces = (const struct XSParseKeywordPieceType []){ |
311
|
|
|
|
|
|
|
XPK_IDENT, |
312
|
|
|
|
|
|
|
XPK_ATTRIBUTES, |
313
|
|
|
|
|
|
|
{0} |
314
|
|
|
|
|
|
|
}, |
315
|
|
|
|
|
|
|
.build = &build_field, |
316
|
|
|
|
|
|
|
}; |
317
|
66
|
|
|
|
|
|
static bool parse_method_permit(pTHX_ void *hookdata) |
318
|
|
|
|
|
|
|
{ |
319
|
66
|
100
|
|
|
|
|
if(!have_compclass_meta) |
320
|
1
|
|
|
|
|
|
croak("Cannot 'method' outside of 'class'"); |
321
|
|
|
|
|
|
|
|
322
|
65
|
50
|
|
|
|
|
if(!sv_eq(PL_curstname, compclass_meta->name)) |
323
|
0
|
|
|
|
|
|
croak("Current package name no longer matches current class (%" SVf " vs %" SVf ")", |
324
|
0
|
|
|
|
|
|
PL_curstname, compclass_meta->name); |
325
|
|
|
|
|
|
|
|
326
|
65
|
|
|
|
|
|
return true; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
65
|
|
|
|
|
|
static void parse_method_pre_subparse(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata) |
330
|
|
|
|
|
|
|
{ |
331
|
|
|
|
|
|
|
/* While creating the new scope CV we need to ENTER a block so as not to |
332
|
|
|
|
|
|
|
* break any interpvars |
333
|
|
|
|
|
|
|
*/ |
334
|
65
|
|
|
|
|
|
ENTER; |
335
|
65
|
|
|
|
|
|
SAVESPTR(PL_comppad); |
336
|
65
|
|
|
|
|
|
SAVESPTR(PL_comppad_name); |
337
|
65
|
|
|
|
|
|
SAVESPTR(PL_curpad); |
338
|
|
|
|
|
|
|
|
339
|
65
|
|
|
|
|
|
intro_my(); |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
MethodMeta *compmethodmeta; |
342
|
65
|
|
|
|
|
|
Newx(compmethodmeta, 1, MethodMeta); |
343
|
|
|
|
|
|
|
|
344
|
130
|
|
|
|
|
|
compmethodmeta->name = SvREFCNT_inc(ctx->name); |
345
|
65
|
|
|
|
|
|
compmethodmeta->class = NULL; |
346
|
65
|
|
|
|
|
|
compmethodmeta->is_common = false; |
347
|
|
|
|
|
|
|
|
348
|
65
|
|
|
|
|
|
hv_stores(ctx->moddata, "Class::Plain/compmethodmeta", newSVuv(PTR2UV(compmethodmeta))); |
349
|
|
|
|
|
|
|
|
350
|
65
|
|
|
|
|
|
LEAVE; |
351
|
65
|
|
|
|
|
|
} |
352
|
|
|
|
|
|
|
|
353
|
18
|
|
|
|
|
|
static bool parse_method_filter_attr(pTHX_ struct XSParseSublikeContext *ctx, SV *attr, SV *val, void *hookdata) |
354
|
|
|
|
|
|
|
{ |
355
|
18
|
50
|
|
|
|
|
MethodMeta *compmethodmeta = NUM2PTR(MethodMeta *, SvUV(*hv_fetchs(ctx->moddata, "Class::Plain/compmethodmeta", 0))); |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
struct MethodAttributeDefinition *def; |
358
|
18
|
50
|
|
|
|
|
for(def = method_attributes; def->attrname; def++) { |
359
|
18
|
50
|
|
|
|
|
if(!strEQ(SvPVX(attr), def->attrname)) |
360
|
0
|
|
|
|
|
|
continue; |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
/* TODO: We might want to wrap the CV in some sort of MethodMeta struct |
363
|
|
|
|
|
|
|
* but for now we'll just pass the XSParseSublikeContext context */ |
364
|
18
|
50
|
|
|
|
|
(*def->apply)(aTHX_ compmethodmeta, SvPOK(val) ? SvPVX(val) : NULL, def->applydata); |
365
|
|
|
|
|
|
|
|
366
|
18
|
|
|
|
|
|
return true; |
367
|
|
|
|
|
|
|
} |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
/* No error, just let it fall back to usual attribute handling */ |
370
|
|
|
|
|
|
|
return false; |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
65
|
|
|
|
|
|
static void parse_method_post_blockstart(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata) |
374
|
|
|
|
|
|
|
{ |
375
|
65
|
50
|
|
|
|
|
MethodMeta *compmethodmeta = NUM2PTR(MethodMeta *, SvUV(*hv_fetchs(ctx->moddata, "Class::Plain/compmethodmeta", 0))); |
376
|
65
|
100
|
|
|
|
|
if(compmethodmeta->is_common) { |
377
|
18
|
|
|
|
|
|
IV var_index = pad_add_name_pvs("$class", 0, NULL, NULL); |
378
|
18
|
50
|
|
|
|
|
if (!(var_index == 1)) { |
379
|
0
|
|
|
|
|
|
croak("[Unexpected]Invalid index of the $class variable:%d", (int)var_index); |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
else { |
383
|
47
|
|
|
|
|
|
IV var_index = pad_add_name_pvs("$self", 0, NULL, NULL); |
384
|
47
|
50
|
|
|
|
|
if(var_index != 1) { |
385
|
0
|
|
|
|
|
|
croak("[Unexpected]Invalid index of the $self variable:%d", (int)var_index); |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
|
389
|
65
|
|
|
|
|
|
intro_my(); |
390
|
65
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
65
|
|
|
|
|
|
static void parse_method_pre_blockend(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata) |
393
|
|
|
|
|
|
|
{ |
394
|
65
|
50
|
|
|
|
|
MethodMeta *compmethodmeta = NUM2PTR(MethodMeta *, SvUV(*hv_fetchs(ctx->moddata, "Class::Plain/compmethodmeta", 0))); |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
/* If we have no ctx->body that means this was a bodyless method |
397
|
|
|
|
|
|
|
* declaration; a required method |
398
|
|
|
|
|
|
|
*/ |
399
|
65
|
100
|
|
|
|
|
if (ctx->body) { |
400
|
61
|
100
|
|
|
|
|
if(compmethodmeta->is_common) { |
401
|
18
|
|
|
|
|
|
ctx->body = op_append_list(OP_LINESEQ, |
402
|
|
|
|
|
|
|
ClassPlain_newCOMMONMETHSTARTOP(aTHX_ 0 | |
403
|
|
|
|
|
|
|
(0)), |
404
|
|
|
|
|
|
|
ctx->body); |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
else { |
407
|
|
|
|
|
|
|
OP *fieldops = NULL, *methstartop; |
408
|
43
|
|
|
|
|
|
fieldops = op_append_list(OP_LINESEQ, fieldops, |
409
|
|
|
|
|
|
|
newSTATEOP(0, NULL, NULL)); |
410
|
43
|
|
|
|
|
|
fieldops = op_append_list(OP_LINESEQ, fieldops, |
411
|
|
|
|
|
|
|
(methstartop = ClassPlain_newMETHSTARTOP(aTHX_ 0 | |
412
|
|
|
|
|
|
|
(0) | |
413
|
|
|
|
|
|
|
(0)))); |
414
|
|
|
|
|
|
|
|
415
|
43
|
|
|
|
|
|
ctx->body = op_append_list(OP_LINESEQ, fieldops, ctx->body); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
} |
418
|
65
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
65
|
|
|
|
|
|
static void parse_method_post_newcv(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata) |
421
|
|
|
|
|
|
|
{ |
422
|
|
|
|
|
|
|
MethodMeta *compmethodmeta; |
423
|
|
|
|
|
|
|
{ |
424
|
65
|
|
|
|
|
|
SV *tmpsv = *hv_fetchs(ctx->moddata, "Class::Plain/compmethodmeta", 0); |
425
|
65
|
50
|
|
|
|
|
compmethodmeta = NUM2PTR(MethodMeta *, SvUV(tmpsv)); |
426
|
65
|
|
|
|
|
|
sv_setuv(tmpsv, 0); |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
65
|
100
|
|
|
|
|
if(ctx->cv) |
430
|
61
|
|
|
|
|
|
CvMETHOD_on(ctx->cv); |
431
|
|
|
|
|
|
|
|
432
|
65
|
100
|
|
|
|
|
if(ctx->cv && ctx->name && (ctx->actions & XS_PARSE_SUBLIKE_ACTION_INSTALL_SYMBOL)) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
433
|
58
|
|
|
|
|
|
MethodMeta *meta = ClassPlain_class_add_method(aTHX_ compclass_meta, ctx->name); |
434
|
|
|
|
|
|
|
|
435
|
58
|
|
|
|
|
|
meta->is_common = compmethodmeta->is_common; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
65
|
|
|
|
|
|
SvREFCNT_dec(compmethodmeta->name); |
439
|
65
|
|
|
|
|
|
Safefree(compmethodmeta); |
440
|
65
|
|
|
|
|
|
} |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
static struct XSParseSublikeHooks parse_method_hooks = { |
443
|
|
|
|
|
|
|
.flags = XS_PARSE_SUBLIKE_FLAG_FILTERATTRS | |
444
|
|
|
|
|
|
|
XS_PARSE_SUBLIKE_COMPAT_FLAG_DYNAMIC_ACTIONS | |
445
|
|
|
|
|
|
|
XS_PARSE_SUBLIKE_FLAG_BODY_OPTIONAL, |
446
|
|
|
|
|
|
|
.permit_hintkey = "Class::Plain/method", |
447
|
|
|
|
|
|
|
.permit = parse_method_permit, |
448
|
|
|
|
|
|
|
.pre_subparse = parse_method_pre_subparse, |
449
|
|
|
|
|
|
|
.filter_attr = parse_method_filter_attr, |
450
|
|
|
|
|
|
|
.post_blockstart = parse_method_post_blockstart, |
451
|
|
|
|
|
|
|
.pre_blockend = parse_method_pre_blockend, |
452
|
|
|
|
|
|
|
.post_newcv = parse_method_post_newcv, |
453
|
|
|
|
|
|
|
}; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
/* internal function shared by various *.c files */ |
456
|
0
|
|
|
|
|
|
void ClassPlain_need_PLparser(pTHX) |
457
|
|
|
|
|
|
|
{ |
458
|
0
|
0
|
|
|
|
|
if(!PL_parser) { |
459
|
|
|
|
|
|
|
/* We need to generate just enough of a PL_parser to keep newSTATEOP() |
460
|
|
|
|
|
|
|
* happy, otherwise it will SIGSEGV (RT133258) |
461
|
|
|
|
|
|
|
*/ |
462
|
0
|
|
|
|
|
|
SAVEVPTR(PL_parser); |
463
|
0
|
|
|
|
|
|
Newxz(PL_parser, 1, yy_parser); |
464
|
0
|
|
|
|
|
|
SAVEFREEPV(PL_parser); |
465
|
|
|
|
|
|
|
|
466
|
0
|
|
|
|
|
|
PL_parser->copline = NOLINE; |
467
|
|
|
|
|
|
|
} |
468
|
0
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
MODULE = Class::Plain PACKAGE = Class::Plain::MetaFunctions |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
BOOT: |
473
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_methstart, xop_name, "methstart"); |
474
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_methstart, xop_desc, "enter method"); |
475
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_methstart, xop_class, OA_BASEOP); |
476
|
7
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ &pp_methstart, &xop_methstart); |
477
|
|
|
|
|
|
|
|
478
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_commonmethstart, xop_name, "commonmethstart"); |
479
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_commonmethstart, xop_desc, "enter method :common"); |
480
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_commonmethstart, xop_class, OA_BASEOP); |
481
|
7
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ &pp_commonmethstart, &xop_commonmethstart); |
482
|
|
|
|
|
|
|
|
483
|
7
|
|
|
|
|
|
boot_xs_parse_keyword(0.22); /* XPK_AUTOSEMI */ |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
register_xs_parse_keyword("class", &kwhooks_class, (void *)0); |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
register_xs_parse_keyword("field", &kwhooks_field, "field"); |
488
|
|
|
|
|
|
|
|
489
|
7
|
|
|
|
|
|
boot_xs_parse_sublike(0.15); /* dynamic actions */ |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
register_xs_parse_sublike("method", &parse_method_hooks, (void *)0); |