| 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); |