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