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_comp_class(pTHX) { |
75
|
203
|
|
|
|
|
|
SV** svp = hv_fetchs(GvHV(PL_hintgv), "Class::Plain/comp_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_comp_class(pTHX) { |
82
|
106
|
|
|
|
|
|
SV** svp = hv_fetchs(GvHV(PL_hintgv), "Class::Plain/comp_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_comp_class_set(pTHX_ ClassMeta *class) { |
93
|
40
|
|
|
|
|
|
SV* sv = *hv_fetchs(GvHV(PL_hintgv), "Class::Plain/comp_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
|
40
|
|
|
|
|
|
static int build_classlike(pTHX_ OP* *out, XSParseKeywordPiece* args[], size_t nargs, void* hookdata) { |
152
|
|
|
|
|
|
|
int argi = 0; |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
|
155
|
40
|
|
|
|
|
|
SV* packagename = args[argi++]->sv; |
156
|
|
|
|
|
|
|
/* Grrr; XPK bug */ |
157
|
40
|
50
|
|
|
|
|
if(!packagename) |
158
|
0
|
|
|
|
|
|
croak("Expected a class name after 'class'"); |
159
|
|
|
|
|
|
|
|
160
|
40
|
|
|
|
|
|
IV type = (IV)(intptr_t)hookdata; |
161
|
|
|
|
|
|
|
|
162
|
40
|
|
|
|
|
|
ClassMeta* class = ClassPlain_create_class(aTHX_ type, packagename); |
163
|
|
|
|
|
|
|
|
164
|
40
|
100
|
|
|
|
|
if (type == 1) { |
165
|
1
|
|
|
|
|
|
class->is_role = 1; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
40
|
|
|
|
|
|
int nattrs = args[argi++]->i; |
169
|
40
|
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
|
40
|
|
|
|
|
|
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
|
40
|
50
|
|
|
|
|
if(lex_consume_unichar('{')) { |
193
|
|
|
|
|
|
|
exists_class_block = true; |
194
|
40
|
|
|
|
|
|
ENTER; |
195
|
|
|
|
|
|
|
} |
196
|
0
|
0
|
|
|
|
|
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
|
40
|
|
|
|
|
|
SAVEGENERICSV(PL_curstash); |
205
|
40
|
|
|
|
|
|
save_item(PL_curstname); |
206
|
|
|
|
|
|
|
|
207
|
40
|
|
|
|
|
|
PL_curstash = (HV *)SvREFCNT_inc(gv_stashsv(class->name, GV_ADD)); |
208
|
40
|
|
|
|
|
|
sv_setsv(PL_curstname, packagename); |
209
|
|
|
|
|
|
|
|
210
|
40
|
|
|
|
|
|
PL_hints |= HINT_BLOCK_SCOPE; |
211
|
40
|
|
|
|
|
|
PL_parser->copline = NOLINE; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
40
|
50
|
|
|
|
|
if (exists_class_block) { |
215
|
40
|
|
|
|
|
|
I32 save_ix = block_start(TRUE); |
216
|
40
|
|
|
|
|
|
S_comp_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
|
|
|
|
|
|
|
|
247
|
40
|
|
|
|
|
|
LEAVE; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
/* CARGOCULT from perl/perly.y:PACKAGE BAREWORD BAREWORD '{' */ |
250
|
|
|
|
|
|
|
/* a block is a loop that happens once */ |
251
|
40
|
|
|
|
|
|
*out = op_append_elem(OP_LINESEQ, |
252
|
|
|
|
|
|
|
newWHILEOP(0, 1, NULL, NULL, body, NULL, 0), |
253
|
|
|
|
|
|
|
newSVOP(OP_CONST, 0, &PL_sv_yes)); |
254
|
40
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
else { |
257
|
0
|
|
|
|
|
|
SAVEHINTS(); |
258
|
0
|
|
|
|
|
|
S_comp_class_set(aTHX_ class); |
259
|
|
|
|
|
|
|
|
260
|
0
|
|
|
|
|
|
*out = newSVOP(OP_CONST, 0, &PL_sv_yes); |
261
|
0
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
static const struct XSParseKeywordPieceType pieces_classlike[] = { |
266
|
|
|
|
|
|
|
XPK_PACKAGENAME, |
267
|
|
|
|
|
|
|
/* This should really a repeated (tagged?) choice of a number of things, but |
268
|
|
|
|
|
|
|
* right now there's only one thing permitted here anyway |
269
|
|
|
|
|
|
|
*/ |
270
|
|
|
|
|
|
|
XPK_ATTRIBUTES, |
271
|
|
|
|
|
|
|
{0} |
272
|
|
|
|
|
|
|
}; |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
static const struct XSParseKeywordHooks kwhooks_class = { |
275
|
|
|
|
|
|
|
.permit_hintkey = "Class::Plain/class", |
276
|
|
|
|
|
|
|
.pieces = pieces_classlike, |
277
|
|
|
|
|
|
|
.build = &build_classlike, |
278
|
|
|
|
|
|
|
}; |
279
|
|
|
|
|
|
|
|
280
|
40
|
|
|
|
|
|
static void check_field(pTHX_ void* hookdata) { |
281
|
|
|
|
|
|
|
char *kwname = hookdata; |
282
|
|
|
|
|
|
|
|
283
|
40
|
100
|
|
|
|
|
if(!S_have_comp_class(aTHX)) |
284
|
2
|
|
|
|
|
|
croak("Cannot '%s' outside of 'class'", kwname); |
285
|
|
|
|
|
|
|
|
286
|
38
|
50
|
|
|
|
|
if(!sv_eq(PL_curstname, S_comp_class(aTHX)->name)) |
287
|
0
|
|
|
|
|
|
croak("Current package name no longer matches current class (%" SVf " vs %" SVf ")", |
288
|
0
|
|
|
|
|
|
PL_curstname, S_comp_class(aTHX)->name); |
289
|
38
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
38
|
|
|
|
|
|
static int build_field(pTHX_ OP* *out, XSParseKeywordPiece* args[], size_t nargs, void* hookdata) { |
292
|
|
|
|
|
|
|
int argi = 0; |
293
|
|
|
|
|
|
|
|
294
|
38
|
|
|
|
|
|
SV* name = args[argi++]->sv; |
295
|
|
|
|
|
|
|
|
296
|
38
|
|
|
|
|
|
FieldMeta *field_class = ClassPlain_class_add_field(aTHX_ S_comp_class(aTHX), name); |
297
|
|
|
|
|
|
|
SvREFCNT_dec(name); |
298
|
|
|
|
|
|
|
|
299
|
38
|
|
|
|
|
|
int nattrs = args[argi++]->i; |
300
|
38
|
100
|
|
|
|
|
if(nattrs) { |
301
|
50
|
100
|
|
|
|
|
while(argi < (nattrs+2)) { |
302
|
26
|
|
|
|
|
|
SV* attrname = args[argi]->attr.name; |
303
|
26
|
|
|
|
|
|
SV* attrval = args[argi]->attr.value; |
304
|
|
|
|
|
|
|
|
305
|
26
|
|
|
|
|
|
inplace_trim_whitespace(attrval); |
306
|
|
|
|
|
|
|
|
307
|
26
|
|
|
|
|
|
ClassPlain_field_apply_attribute(aTHX_ field_class, SvPVX(attrname), attrval); |
308
|
|
|
|
|
|
|
|
309
|
26
|
50
|
|
|
|
|
if(attrval) |
310
|
|
|
|
|
|
|
SvREFCNT_dec(attrval); |
311
|
|
|
|
|
|
|
|
312
|
26
|
|
|
|
|
|
argi++; |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
38
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
static const struct XSParseKeywordHooks kwhooks_field = { |
320
|
|
|
|
|
|
|
.flags = XPK_FLAG_STMT, |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
.check = &check_field, |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
.permit_hintkey = "Class::Plain/field", |
325
|
|
|
|
|
|
|
.pieces = (const struct XSParseKeywordPieceType []){ |
326
|
|
|
|
|
|
|
XPK_IDENT, |
327
|
|
|
|
|
|
|
XPK_ATTRIBUTES, |
328
|
|
|
|
|
|
|
{0} |
329
|
|
|
|
|
|
|
}, |
330
|
|
|
|
|
|
|
.build = &build_field, |
331
|
|
|
|
|
|
|
}; |
332
|
66
|
|
|
|
|
|
static bool parse_method_permit(pTHX_ void* hookdata) |
333
|
|
|
|
|
|
|
{ |
334
|
66
|
100
|
|
|
|
|
if(!S_have_comp_class(aTHX)) |
335
|
1
|
|
|
|
|
|
croak("Cannot 'method' outside of 'class'"); |
336
|
|
|
|
|
|
|
|
337
|
65
|
50
|
|
|
|
|
if(!sv_eq(PL_curstname, S_comp_class(aTHX)->name)) |
338
|
0
|
|
|
|
|
|
croak("Current package name no longer matches current class (%" SVf " vs %" SVf ")", |
339
|
0
|
|
|
|
|
|
PL_curstname, S_comp_class(aTHX)->name); |
340
|
|
|
|
|
|
|
|
341
|
65
|
|
|
|
|
|
return true; |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
65
|
|
|
|
|
|
static void parse_method_pre_subparse(pTHX_ struct XSParseSublikeContext* ctx, void* hookdata) { |
345
|
|
|
|
|
|
|
/* While creating the new scope CV we need to ENTER a block so as not to |
346
|
|
|
|
|
|
|
* break any interpvars |
347
|
|
|
|
|
|
|
*/ |
348
|
65
|
|
|
|
|
|
ENTER; |
349
|
65
|
|
|
|
|
|
SAVESPTR(PL_comppad); |
350
|
65
|
|
|
|
|
|
SAVESPTR(PL_comppad_name); |
351
|
65
|
|
|
|
|
|
SAVESPTR(PL_curpad); |
352
|
|
|
|
|
|
|
|
353
|
65
|
|
|
|
|
|
intro_my(); |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
MethodMeta* comp_method; |
356
|
65
|
|
|
|
|
|
Newxz(comp_method, 1, MethodMeta); |
357
|
|
|
|
|
|
|
|
358
|
130
|
|
|
|
|
|
comp_method->name = SvREFCNT_inc(ctx->name); |
359
|
|
|
|
|
|
|
|
360
|
65
|
|
|
|
|
|
hv_stores(ctx->moddata, "Class::Plain/comp_method", newSVuv(PTR2UV(comp_method))); |
361
|
|
|
|
|
|
|
|
362
|
65
|
|
|
|
|
|
LEAVE; |
363
|
65
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
18
|
|
|
|
|
|
static bool parse_method_filter_attr(pTHX_ struct XSParseSublikeContext* ctx, SV* attr, SV* val, void* hookdata) { |
366
|
18
|
50
|
|
|
|
|
MethodMeta* comp_method = NUM2PTR(MethodMeta* , SvUV(*hv_fetchs(ctx->moddata, "Class::Plain/comp_method", 0))); |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
struct MethodAttributeDefinition *def; |
369
|
18
|
50
|
|
|
|
|
for(def = method_attributes; def->attrname; def++) { |
370
|
18
|
50
|
|
|
|
|
if(!strEQ(SvPVX(attr), def->attrname)) |
371
|
0
|
|
|
|
|
|
continue; |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
/* TODO: We might want to wrap the CV in some sort of MethodMeta struct |
374
|
|
|
|
|
|
|
* but for now we'll just pass the XSParseSublikeContext context */ |
375
|
18
|
50
|
|
|
|
|
(*def->apply)(aTHX_ comp_method, SvPOK(val) ? SvPVX(val) : NULL, def->applydata); |
376
|
|
|
|
|
|
|
|
377
|
18
|
|
|
|
|
|
return true; |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
/* No error, just let it fall back to usual attribute handling */ |
381
|
|
|
|
|
|
|
return false; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
65
|
|
|
|
|
|
static void parse_method_post_blockstart(pTHX_ struct XSParseSublikeContext* ctx, void* hookdata) { |
385
|
|
|
|
|
|
|
|
386
|
65
|
50
|
|
|
|
|
MethodMeta* comp_method = NUM2PTR(MethodMeta* , SvUV(*hv_fetchs(ctx->moddata, "Class::Plain/comp_method", 0))); |
387
|
65
|
100
|
|
|
|
|
if(comp_method->is_common) { |
388
|
18
|
|
|
|
|
|
IV var_index = pad_add_name_pvs("$class", 0, NULL, NULL); |
389
|
18
|
50
|
|
|
|
|
if (!(var_index == 1)) { |
390
|
0
|
|
|
|
|
|
croak("[Unexpected]Invalid index of the $class variable:%d", (int)var_index); |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
} |
393
|
|
|
|
|
|
|
else { |
394
|
47
|
|
|
|
|
|
IV var_index = pad_add_name_pvs("$self", 0, NULL, NULL); |
395
|
47
|
50
|
|
|
|
|
if(var_index != 1) { |
396
|
0
|
|
|
|
|
|
croak("[Unexpected]Invalid index of the $self variable:%d", (int)var_index); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
} |
399
|
|
|
|
|
|
|
|
400
|
65
|
|
|
|
|
|
intro_my(); |
401
|
65
|
|
|
|
|
|
} |
402
|
|
|
|
|
|
|
|
403
|
65
|
|
|
|
|
|
static void parse_method_pre_blockend(pTHX_ struct XSParseSublikeContext* ctx, void* hookdata) { |
404
|
|
|
|
|
|
|
|
405
|
65
|
50
|
|
|
|
|
MethodMeta* comp_method = NUM2PTR(MethodMeta* , SvUV(*hv_fetchs(ctx->moddata, "Class::Plain/comp_method", 0))); |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
/* If we have no ctx->body that means this was a bodyless method |
408
|
|
|
|
|
|
|
* declaration; a required method |
409
|
|
|
|
|
|
|
*/ |
410
|
65
|
100
|
|
|
|
|
if (ctx->body) { |
411
|
61
|
100
|
|
|
|
|
if(comp_method->is_common) { |
412
|
18
|
|
|
|
|
|
ctx->body = op_append_list(OP_LINESEQ, |
413
|
|
|
|
|
|
|
ClassPlain_newCOMMONMETHSTARTOP(aTHX_ 0 | |
414
|
|
|
|
|
|
|
(0)), |
415
|
|
|
|
|
|
|
ctx->body); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
else { |
418
|
|
|
|
|
|
|
OP* fieldops = NULL, *methstartop; |
419
|
43
|
|
|
|
|
|
fieldops = op_append_list(OP_LINESEQ, fieldops, |
420
|
|
|
|
|
|
|
newSTATEOP(0, NULL, NULL)); |
421
|
43
|
|
|
|
|
|
fieldops = op_append_list(OP_LINESEQ, fieldops, |
422
|
|
|
|
|
|
|
(methstartop = ClassPlain_newMETHSTARTOP(aTHX_ 0 | |
423
|
|
|
|
|
|
|
(0) | |
424
|
|
|
|
|
|
|
(0)))); |
425
|
|
|
|
|
|
|
|
426
|
43
|
|
|
|
|
|
ctx->body = op_append_list(OP_LINESEQ, fieldops, ctx->body); |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} |
429
|
65
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
65
|
|
|
|
|
|
static void parse_method_post_newcv(pTHX_ struct XSParseSublikeContext* ctx, void* hookdata) { |
432
|
|
|
|
|
|
|
MethodMeta* comp_method; |
433
|
|
|
|
|
|
|
{ |
434
|
65
|
|
|
|
|
|
SV* tmpsv = *hv_fetchs(ctx->moddata, "Class::Plain/comp_method", 0); |
435
|
65
|
50
|
|
|
|
|
comp_method = NUM2PTR(MethodMeta* , SvUV(tmpsv)); |
436
|
65
|
|
|
|
|
|
sv_setuv(tmpsv, 0); |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
65
|
100
|
|
|
|
|
if(ctx->cv) { |
440
|
61
|
|
|
|
|
|
CvMETHOD_on(ctx->cv); |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
65
|
100
|
|
|
|
|
if(ctx->name && (ctx->actions & XS_PARSE_SUBLIKE_ACTION_INSTALL_SYMBOL)) { |
|
|
50
|
|
|
|
|
|
444
|
62
|
|
|
|
|
|
MethodMeta* method = ClassPlain_class_add_method(aTHX_ S_comp_class(aTHX), ctx->name); |
445
|
62
|
|
|
|
|
|
method->is_common = comp_method->is_common; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
// "sub foo;" means requred method in roles. |
448
|
62
|
50
|
|
|
|
|
if (!ctx->body) { |
449
|
62
|
|
|
|
|
|
method->is_required = 1; |
450
|
|
|
|
|
|
|
|
451
|
62
|
50
|
|
|
|
|
if (method->class->is_role) { |
452
|
|
|
|
|
|
|
if (method->is_required) { |
453
|
|
|
|
|
|
|
// The source code of Role::Tiny->import |
454
|
0
|
|
|
|
|
|
SV* sv_source_code = sv_2mortal(newSVpv("", 0)); |
455
|
0
|
|
|
|
|
|
sv_catpv(sv_source_code, "{\n"); |
456
|
0
|
|
|
|
|
|
sv_catpv(sv_source_code, " package "); |
457
|
0
|
0
|
|
|
|
|
sv_catpv(sv_source_code, SvPV_nolen(method->class->name)); |
458
|
0
|
|
|
|
|
|
sv_catpv(sv_source_code, ";\n"); |
459
|
0
|
|
|
|
|
|
sv_catpv(sv_source_code, " requires('"); |
460
|
0
|
0
|
|
|
|
|
sv_catpv(sv_source_code, SvPV_nolen(method->name)); |
461
|
0
|
|
|
|
|
|
sv_catpv(sv_source_code, "');\n"); |
462
|
0
|
|
|
|
|
|
sv_catpv(sv_source_code, "}\n"); |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
// Role::Tiny->import |
465
|
0
|
0
|
|
|
|
|
Perl_eval_pv(aTHX_ SvPV_nolen(sv_source_code), 1); |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
65
|
|
|
|
|
|
SvREFCNT_dec(comp_method->name); |
472
|
65
|
|
|
|
|
|
Safefree(comp_method); |
473
|
65
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
static struct XSParseSublikeHooks parse_method_hooks = { |
476
|
|
|
|
|
|
|
.flags = XS_PARSE_SUBLIKE_FLAG_FILTERATTRS | |
477
|
|
|
|
|
|
|
XS_PARSE_SUBLIKE_COMPAT_FLAG_DYNAMIC_ACTIONS | |
478
|
|
|
|
|
|
|
XS_PARSE_SUBLIKE_FLAG_BODY_OPTIONAL, |
479
|
|
|
|
|
|
|
.permit_hintkey = "Class::Plain/method", |
480
|
|
|
|
|
|
|
.permit = parse_method_permit, |
481
|
|
|
|
|
|
|
.pre_subparse = parse_method_pre_subparse, |
482
|
|
|
|
|
|
|
.filter_attr = parse_method_filter_attr, |
483
|
|
|
|
|
|
|
.post_blockstart = parse_method_post_blockstart, |
484
|
|
|
|
|
|
|
.pre_blockend = parse_method_pre_blockend, |
485
|
|
|
|
|
|
|
.post_newcv = parse_method_post_newcv, |
486
|
|
|
|
|
|
|
}; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
/* internal function shared by various *.c files */ |
489
|
0
|
|
|
|
|
|
void ClassPlain_need_PLparser(pTHX) |
490
|
|
|
|
|
|
|
{ |
491
|
0
|
0
|
|
|
|
|
if(!PL_parser) { |
492
|
|
|
|
|
|
|
/* We need to generate just enough of a PL_parser to keep newSTATEOP() |
493
|
|
|
|
|
|
|
* happy, otherwise it will SIGSEGV (RT133258) |
494
|
|
|
|
|
|
|
*/ |
495
|
0
|
|
|
|
|
|
SAVEVPTR(PL_parser); |
496
|
0
|
|
|
|
|
|
Newxz(PL_parser, 1, yy_parser); |
497
|
0
|
|
|
|
|
|
SAVEFREEPV(PL_parser); |
498
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
|
PL_parser->copline = NOLINE; |
500
|
|
|
|
|
|
|
} |
501
|
0
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
MODULE = Class::Plain PACKAGE = Class::Plain::MetaFunctions |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
BOOT: |
506
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_methstart, xop_name, "methstart"); |
507
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_methstart, xop_desc, "enter method"); |
508
|
7
|
|
|
|
|
|
XopENTRY_set(&xop_methstart, xop_class, OA_BASEOP); |
509
|
7
|
|
|
|
|
|
Perl_custom_op_register(aTHX_ &pp_methstart, &xop_methstart); |
510
|
|
|
|
|
|
|
|
511
|
7
|
|
|
|
|
|
boot_xs_parse_keyword(0.22); /* XPK_AUTOSEMI */ |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
register_xs_parse_keyword("class", &kwhooks_class, (void*)0); |
514
|
|
|
|
|
|
|
register_xs_parse_keyword("role", &kwhooks_class, (void*)METATYPE_ROLE); |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
register_xs_parse_keyword("field", &kwhooks_field, "field"); |
517
|
|
|
|
|
|
|
|
518
|
7
|
|
|
|
|
|
boot_xs_parse_sublike(0.15); /* dynamic actions */ |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
register_xs_parse_sublike("method", &parse_method_hooks, (void*)0); |