1 /* You may distribute under the terms of either the GNU General Public License
2 * or the Artistic License (the same terms as Perl itself)
3 *
4 * (C) Paul Evans, 2019-2021 -- leonerd@leonerd.org.uk
5 */
6 #define PERL_NO_GET_CONTEXT
7
8 #include "EXTERN.h"
9 #include "perl.h"
10 #include "XSUB.h"
11
12 #include "XSParseKeyword.h"
13
14 #include "XSParseSublike.h"
15
16 #include "perl-backcompat.c.inc"
17 #include "sv_setrv.c.inc"
18
19 #ifdef HAVE_DMD_HELPER
20 # include "DMD_helper.h"
21 #endif
22
23 #include "perl-additions.c.inc"
24 #include "forbid_outofblock_ops.c.inc"
25 #include "force_list_keeping_pushmark.c.inc"
26 #include "optree-additions.c.inc"
27 #include "newOP_CUSTOM.c.inc"
28
29 #if HAVE_PERL_VERSION(5, 26, 0)
30 # define HAVE_PARSE_SUBSIGNATURE
31 #endif
32
33 #if HAVE_PERL_VERSION(5, 28, 0)
34 # define HAVE_UNOP_AUX_PV
35 #endif
36
37 #include "object_pad.h"
38 #include "class.h"
39 #include "slot.h"
40
41 typedef void AttributeHandler(pTHX_ void *target, const char *value, void *data);
42
43 struct AttributeDefinition {
44 char *attrname;
45 /* TODO: int flags */
46 AttributeHandler *apply;
47 void *applydata;
48 };
49
50 /*********************************
51 * Class and Slot Implementation *
52 *********************************/
53
54 /* Empty role embedding that is applied to all invokable role methods */
55 static RoleEmbedding embedding_standalone = {};
56
ObjectPad_extend_pad_vars(pTHX_ const ClassMeta * meta)57 void ObjectPad_extend_pad_vars(pTHX_ const ClassMeta *meta)
58 {
59 PADOFFSET padix;
60
61 padix = pad_add_name_pvs("$self", 0, NULL, NULL);
62 if(padix != PADIX_SELF)
63 croak("ARGH: Expected that padix[$self] = 1");
64
65 /* Give it a name that isn't valid as a Perl variable so it can't collide */
66 padix = pad_add_name_pvs("@(Object::Pad/slots)", 0, NULL, NULL);
67 if(padix != PADIX_SLOTS)
68 croak("ARGH: Expected that padix[@slots] = 2");
69
70 if(meta->type == METATYPE_ROLE) {
71 /* Don't give this a padname or Future::AsyncAwait will break it (RT137649) */
72 padix = pad_add_name_pvs("", 0, NULL, NULL);
73 if(padix != PADIX_EMBEDDING)
74 croak("ARGH: Expected that padix[(embedding)] = 3");
75 }
76 }
77
78 #define find_padix_for_slot(slotmeta) S_find_padix_for_slot(aTHX_ slotmeta)
S_find_padix_for_slot(pTHX_ SlotMeta * slotmeta)79 static PADOFFSET S_find_padix_for_slot(pTHX_ SlotMeta *slotmeta)
80 {
81 const char *slotname = SvPVX(slotmeta->name);
82 #if HAVE_PERL_VERSION(5, 20, 0)
83 const PADNAMELIST *nl = PadlistNAMES(CvPADLIST(PL_compcv));
84 PADNAME **names = PadnamelistARRAY(nl);
85 PADOFFSET padix;
86
87 for(padix = 1; padix <= PadnamelistMAXNAMED(nl); padix++) {
88 PADNAME *name = names[padix];
89
90 if(!name || !PadnameLEN(name))
91 continue;
92
93 const char *pv = PadnamePV(name);
94 if(!pv)
95 continue;
96
97 /* slot names are all OUTER vars. This is necessary so we don't get
98 * confused by signatures params of the same name
99 * https://rt.cpan.org/Ticket/Display.html?id=134456
100 */
101 if(!PadnameOUTER(name))
102 continue;
103 if(!strEQ(pv, slotname))
104 continue;
105
106 /* TODO: for extra robustness we could compare the SV * in the pad itself */
107
108 return padix;
109 }
110
111 return NOT_IN_PAD;
112 #else
113 /* Before the new pad API, the best we can do is call pad_findmy_pv()
114 * It won't get confused about signatures params because these perls are too
115 * old for signatures anyway
116 */
117 return pad_findmy_pv(slotname, 0);
118 #endif
119 }
120
121 static XOP xop_methstart;
pp_methstart(pTHX)122 static OP *pp_methstart(pTHX)
123 {
124 SV *self = av_shift(GvAV(PL_defgv));
125 bool create = PL_op->op_flags & OPf_MOD;
126 bool is_role = PL_op->op_flags & OPf_SPECIAL;
127
128 if(!SvROK(self) || !SvOBJECT(SvRV(self)))
129 croak("Cannot invoke method on a non-instance");
130
131 HV *classstash;
132 SLOTOFFSET offset;
133 RoleEmbedding *embedding = NULL;
134
135 if(is_role) {
136 /* Embedding info is stored in pad1; PAD_SVl() will look at CvDEPTH. We'll
137 * have to grab it manually */
138 PAD *pad1 = PadlistARRAY(CvPADLIST(find_runcv(0)))[1];
139 SV *embeddingsv = PadARRAY(pad1)[PADIX_EMBEDDING];
140
141 if(embeddingsv && embeddingsv != &PL_sv_undef &&
142 (embedding = (RoleEmbedding *)SvPVX(embeddingsv))) {
143 if(embedding == &embedding_standalone) {
144 classstash = NULL;
145 offset = 0;
146 }
147 else {
148 classstash = embedding->classmeta->stash;
149 offset = embedding->offset;
150 }
151 }
152 else {
153 croak("Cannot invoke a role method directly");
154 }
155 }
156 else {
157 classstash = CvSTASH(find_runcv(0));
158 offset = 0;
159 }
160
161 if(classstash) {
162 if(!HvNAME(classstash) || !sv_derived_from_hv(self, classstash))
163 croak("Cannot invoke foreign method on non-derived instance");
164 }
165
166 save_clearsv(&PAD_SVl(PADIX_SELF));
167 sv_setsv(PAD_SVl(PADIX_SELF), self);
168
169 SV *slotsav;
170
171 if(is_role) {
172 if(embedding == &embedding_standalone) {
173 slotsav = NULL;
174 }
175 else {
176 SV *instancedata = get_obj_slotsav(self, embedding->classmeta->repr, create);
177
178 if(create) {
179 slotsav = instancedata;
180 SvREFCNT_inc(slotsav);
181 }
182 else {
183 slotsav = (SV *)newAV();
184 /* MASSIVE CHEAT */
185 AvARRAY(slotsav) = AvARRAY(instancedata) + offset;
186 AvFILLp(slotsav) = AvFILLp(instancedata) - offset;
187 AvREAL_off(slotsav);
188 }
189 }
190 }
191 else {
192 /* op_private contains the repr type so we can extract slots */
193 slotsav = get_obj_slotsav(self, PL_op->op_private, create);
194 SvREFCNT_inc(slotsav);
195 }
196
197 if(slotsav) {
198 SAVESPTR(PAD_SVl(PADIX_SLOTS));
199 PAD_SVl(PADIX_SLOTS) = slotsav;
200 save_freesv(slotsav);
201 }
202
203 return PL_op->op_next;
204 }
205
ObjectPad_newMETHSTARTOP(pTHX_ U32 flags)206 OP *ObjectPad_newMETHSTARTOP(pTHX_ U32 flags)
207 {
208 OP *op = newOP_CUSTOM(&pp_methstart, flags);
209 op->op_private = (U8)(flags >> 8);
210 return op;
211 }
212
213 static XOP xop_slotpad;
pp_slotpad(pTHX)214 static OP *pp_slotpad(pTHX)
215 {
216 #ifdef HAVE_UNOP_AUX
217 SLOTOFFSET slotix = PTR2IV(cUNOP_AUX->op_aux);
218 #else
219 UNOP_with_IV *op = (UNOP_with_IV *)PL_op;
220 SLOTOFFSET slotix = op->iv;
221 #endif
222 PADOFFSET targ = PL_op->op_targ;
223
224 if(SvTYPE(PAD_SV(PADIX_SLOTS)) != SVt_PVAV)
225 croak("ARGH: expected ARRAY of slots at PADIX_SLOTS");
226
227 AV *slotsav = (AV *)PAD_SV(PADIX_SLOTS);
228
229 if(slotix > av_top_index(slotsav))
230 croak("ARGH: instance does not have a slot at index %ld", (long int)slotix);
231
232 SV **slots = AvARRAY(slotsav);
233
234 SV *slot = slots[slotix];
235
236 SV *val;
237 switch(PL_op->op_private) {
238 case OPpSLOTPAD_SV:
239 val = slot;
240 break;
241 case OPpSLOTPAD_AV:
242 if(!SvROK(slot) || SvTYPE(val = SvRV(slot)) != SVt_PVAV)
243 croak("ARGH: expected to find an ARRAY reference at slot index %ld", (long int)slotix);
244 break;
245 case OPpSLOTPAD_HV:
246 if(!SvROK(slot) || SvTYPE(val = SvRV(slot)) != SVt_PVHV)
247 croak("ARGH: expected to find a HASH reference at slot index %ld", (long int)slotix);
248 break;
249 default:
250 croak("ARGH: unsure what to do with this slot type");
251 }
252
253 SAVESPTR(PAD_SVl(targ));
254 PAD_SVl(targ) = SvREFCNT_inc(val);
255 save_freesv(val);
256
257 return PL_op->op_next;
258 }
259
ObjectPad_newSLOTPADOP(pTHX_ U32 flags,PADOFFSET padix,SLOTOFFSET slotix)260 OP *ObjectPad_newSLOTPADOP(pTHX_ U32 flags, PADOFFSET padix, SLOTOFFSET slotix)
261 {
262 #ifdef HAVE_UNOP_AUX
263 OP *op = newUNOP_AUX(OP_CUSTOM, flags, NULL, NUM2PTR(UNOP_AUX_item *, slotix));
264 #else
265 OP *op = newUNOP_with_IV(OP_CUSTOM, flags, NULL, slotix);
266 #endif
267 op->op_targ = padix;
268 op->op_private = (U8)(flags >> 8);
269 op->op_ppaddr = &pp_slotpad;
270
271 return op;
272 }
273
274 /* The metadata on the currently-compiling class */
275 #define compclassmeta S_compclassmeta(aTHX)
S_compclassmeta(pTHX)276 static ClassMeta *S_compclassmeta(pTHX)
277 {
278 SV **svp = hv_fetchs(GvHV(PL_hintgv), "Object::Pad/compclassmeta", 0);
279 if(!svp || !*svp || !SvOK(*svp))
280 return NULL;
281 return (ClassMeta *)SvIV(*svp);
282 }
283
284 #define have_compclassmeta S_have_compclassmeta(aTHX)
S_have_compclassmeta(pTHX)285 static bool S_have_compclassmeta(pTHX)
286 {
287 SV **svp = hv_fetchs(GvHV(PL_hintgv), "Object::Pad/compclassmeta", 0);
288 if(!svp || !*svp)
289 return false;
290
291 if(SvOK(*svp) && SvIV(*svp))
292 return true;
293
294 return false;
295 }
296
297 #define compclassmeta_set(meta) S_compclassmeta_set(aTHX_ meta)
S_compclassmeta_set(pTHX_ ClassMeta * meta)298 static void S_compclassmeta_set(pTHX_ ClassMeta *meta)
299 {
300 SV *sv = *hv_fetchs(GvHV(PL_hintgv), "Object::Pad/compclassmeta", GV_ADD);
301 sv_setiv(sv, (IV)meta);
302 }
303
XS_INTERNAL(xsub_mop_class_seal)304 XS_INTERNAL(xsub_mop_class_seal)
305 {
306 dXSARGS;
307 ClassMeta *meta = XSANY.any_ptr;
308
309 PERL_UNUSED_ARG(items);
310
311 if(!PL_parser) {
312 /* We need to generate just enough of a PL_parser to keep newSTATEOP()
313 * happy, otherwise it will SIGSEGV
314 */
315 SAVEVPTR(PL_parser);
316 Newxz(PL_parser, 1, yy_parser);
317 SAVEFREEPV(PL_parser);
318
319 PL_parser->copline = NOLINE;
320 #if HAVE_PERL_VERSION(5, 20, 0)
321 PL_parser->preambling = NOLINE;
322 #endif
323 }
324
325 mop_class_seal(meta);
326 }
327
328 #define is_valid_ident_utf8(s) S_is_valid_ident_utf8(aTHX_ s)
S_is_valid_ident_utf8(pTHX_ const U8 * s)329 static bool S_is_valid_ident_utf8(pTHX_ const U8 *s)
330 {
331 const U8 *e = s + strlen((char *)s);
332
333 if(!isIDFIRST_utf8_safe(s, e))
334 return false;
335
336 s += UTF8SKIP(s);
337 while(*s) {
338 if(!isIDCONT_utf8_safe(s, e))
339 return false;
340 s += UTF8SKIP(s);
341 }
342
343 return true;
344 }
345
inplace_trim_whitespace(SV * sv)346 void inplace_trim_whitespace(SV *sv)
347 {
348 if(!SvPOK(sv) || !SvCUR(sv))
349 return;
350
351 char *dst = SvPVX(sv);
352 char *src = dst;
353
354 while(*src && isSPACE(*src))
355 src++;
356
357 if(src > dst) {
358 size_t offset = src - dst;
359 Move(src, dst, SvCUR(sv) - offset, char);
360 SvCUR(sv) -= offset;
361 }
362
363 src = dst + SvCUR(sv) - 1;
364 while(src > dst && isSPACE(*src))
365 src--;
366
367 SvCUR(sv) = src - dst + 1;
368 dst[SvCUR(sv)] = 0;
369 }
370
S_check_method_override(pTHX_ struct XSParseSublikeContext * ctx,const char * val,void * _data)371 static void S_check_method_override(pTHX_ struct XSParseSublikeContext *ctx, const char *val, void *_data)
372 {
373 if(!ctx->name)
374 croak("Cannot apply :override to anonymous methods");
375
376 GV *gv = gv_fetchmeth_sv(compclassmeta->stash, ctx->name, 0, 0);
377 if(gv && GvCV(gv))
378 return;
379
380 croak("Superclass does not have a method named '%" SVf "'", SVfARG(ctx->name));
381 }
382
383 static struct AttributeDefinition method_attributes[] = {
384 { "override", (AttributeHandler *)&S_check_method_override, NULL },
385 { 0 }
386 };
387
388 /*******************
389 * Custom Keywords *
390 *******************/
391
build_classlike(pTHX_ OP ** out,XSParseKeywordPiece * args[],size_t nargs,void * hookdata)392 static int build_classlike(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
393 {
394 int argi = 0;
395
396 SV *packagename = args[argi++]->sv;
397 /* Grrr; XPK bug */
398 if(!packagename)
399 croak("Expected a class name after 'class'");
400
401 enum MetaType type = (enum MetaType)hookdata;
402
403 SV *packagever = args[argi++]->sv;
404
405 SV *superclassname = NULL;
406
407 if(args[argi++]->i) {
408 /* extends */
409 argi++; /* ignore the XPK_CHOICE() integer; `extends` and `isa` are synonyms */
410 if(type != METATYPE_CLASS)
411 croak("Only a class may extend another");
412
413 if(superclassname)
414 croak("Multiple superclasses are not currently supported");
415
416 superclassname = args[argi++]->sv;
417 if(!superclassname)
418 croak("Expected a superclass name after 'isa'");
419
420 SV *superclassver = args[argi++]->sv;
421
422 HV *superstash = gv_stashsv(superclassname, 0);
423 if(!superstash || !hv_fetchs(superstash, "new", 0)) {
424 /* Try to `require` the module then attempt a second time */
425 /* load_module() will modify the name argument and take ownership of it */
426 load_module(PERL_LOADMOD_NOIMPORT, newSVsv(superclassname), NULL, NULL);
427 superstash = gv_stashsv(superclassname, 0);
428 }
429
430 if(!superstash)
431 croak("Superclass %" SVf " does not exist", superclassname);
432
433 if(superclassver)
434 ensure_module_version(superclassname, superclassver);
435 }
436
437 ClassMeta *meta = mop_create_class(type, packagename);
438
439 if(superclassname && SvOK(superclassname))
440 mop_class_set_superclass(meta, superclassname);
441
442 int nimplements = args[argi++]->i;
443 if(nimplements) {
444 int i;
445 for(i = 0; i < nimplements; i++) {
446 argi++; /* ignore the XPK_CHOICE() integer; `implements` and `does` are synonyms */
447 int nroles = args[argi++]->i;
448 while(nroles--) {
449 SV *rolename = args[argi++]->sv;
450 if(!rolename)
451 croak("Expected a role name after 'does'");
452
453 SV *rolever = args[argi++]->sv;
454
455 mop_class_load_and_add_role(meta, rolename, rolever);
456 }
457 }
458 }
459
460 if(superclassname)
461 SvREFCNT_dec(superclassname);
462
463 int nattrs = args[argi++]->i;
464 if(nattrs) {
465 int i;
466 for(i = 0; i < nattrs; i++) {
467 SV *attrname = args[argi]->attr.name;
468 SV *attrval = args[argi]->attr.value;
469
470 inplace_trim_whitespace(attrval);
471
472 mop_class_apply_attribute(meta, SvPVX(attrname), attrval);
473
474 argi++;
475 }
476 }
477
478 mop_class_begin(meta);
479
480 /* At this point XS::Parse::Keyword has parsed all it can. From here we will
481 * take over to perform the odd "block or statement" behaviour of `class`
482 * keywords
483 */
484
485 bool is_block;
486
487 if(lex_consume_unichar('{')) {
488 is_block = true;
489 ENTER;
490 }
491 else if(lex_consume_unichar(';')) {
492 is_block = false;
493 }
494 else
495 croak("Expected a block or ';'");
496
497 import_pragma("strict", NULL);
498 import_pragma("warnings", NULL);
499 #if HAVE_PERL_VERSION(5, 31, 9)
500 import_pragma("-feature", "indirect");
501 #else
502 import_pragma("-indirect", ":fatal");
503 #endif
504 #ifdef HAVE_PARSE_SUBSIGNATURE
505 import_pragma("experimental", "signatures");
506 #endif
507
508 /* CARGOCULT from perl/op.c:Perl_package() */
509 {
510 SAVEGENERICSV(PL_curstash);
511 save_item(PL_curstname);
512
513 PL_curstash = (HV *)SvREFCNT_inc(meta->stash);
514 sv_setsv(PL_curstname, packagename);
515
516 PL_hints |= HINT_BLOCK_SCOPE;
517 PL_parser->copline = NOLINE;
518 }
519
520 if(packagever) {
521 /* stolen from op.c because Perl_package_version isn't exported */
522 U32 savehints = PL_hints;
523 PL_hints &= ~HINT_STRICT_VARS;
524
525 sv_setsv(GvSV(gv_fetchpvs("VERSION", GV_ADDMULTI, SVt_PV)), packagever);
526
527 PL_hints = savehints;
528 }
529
530 if(is_block) {
531 I32 save_ix = block_start(TRUE);
532 compclassmeta_set(meta);
533
534 OP *body = parse_stmtseq(0);
535 body = block_end(save_ix, body);
536
537 if(!lex_consume_unichar('}'))
538 croak("Expected }");
539
540 mop_class_seal(meta);
541
542 LEAVE;
543
544 /* CARGOCULT from perl/perly.y:PACKAGE BAREWORD BAREWORD '{' */
545 /* a block is a loop that happens once */
546 *out = op_append_elem(OP_LINESEQ,
547 newWHILEOP(0, 1, NULL, NULL, body, NULL, 0),
548 newSVOP(OP_CONST, 0, &PL_sv_yes));
549 return KEYWORD_PLUGIN_STMT;
550 }
551 else {
552 SAVEDESTRUCTOR_X(&ObjectPad_mop_class_seal, meta);
553
554 SAVEHINTS();
555 compclassmeta_set(meta);
556
557 *out = newSVOP(OP_CONST, 0, &PL_sv_yes);
558 return KEYWORD_PLUGIN_STMT;
559 }
560 }
561
562 static const struct XSParseKeywordPieceType pieces_classlike[] = {
563 XPK_PACKAGENAME,
564 XPK_VSTRING_OPT,
565 XPK_OPTIONAL(
566 XPK_CHOICE( XPK_LITERAL("extends"), XPK_LITERAL("isa") ), XPK_PACKAGENAME, XPK_VSTRING_OPT
567 ),
568 /* This should really a repeated (tagged?) choice of a number of things, but
569 * right now there's only one thing permitted here anyway
570 */
571 XPK_REPEATED(
572 XPK_CHOICE( XPK_LITERAL("implements"), XPK_LITERAL("does") ), XPK_COMMALIST( XPK_PACKAGENAME, XPK_VSTRING_OPT )
573 ),
574 XPK_ATTRIBUTES,
575 {0}
576 };
577
578 static const struct XSParseKeywordHooks kwhooks_class = {
579 .permit_hintkey = "Object::Pad/class",
580 .pieces = pieces_classlike,
581 .build = &build_classlike,
582 };
583 static const struct XSParseKeywordHooks kwhooks_role = {
584 .permit_hintkey = "Object::Pad/role",
585 .pieces = pieces_classlike,
586 .build = &build_classlike,
587 };
588
check_has(pTHX_ void * hookdata)589 static void check_has(pTHX_ void *hookdata)
590 {
591 if(!have_compclassmeta)
592 croak("Cannot 'has' outside of 'class'");
593
594 if(compclassmeta->role_is_invokable)
595 croak("Cannot add slot data to an invokable role");
596 }
597
build_has(pTHX_ OP ** out,XSParseKeywordPiece * args[],size_t nargs,void * hookdata)598 static int build_has(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
599 {
600 int argi = 0;
601
602 SV *name = args[argi++]->sv;
603 char sigil = SvPV_nolen(name)[0];
604
605 SlotMeta *slotmeta = mop_class_add_slot(compclassmeta, name);
606 SvREFCNT_dec(name);
607
608 int nattrs = args[argi++]->i;
609 if(nattrs) {
610 SV *slotmetasv = newSV(0);
611 sv_setref_uv(slotmetasv, "Object::Pad::MOP::Slot", PTR2UV(slotmeta));
612 SAVEFREESV(slotmetasv);
613
614 while(argi < (nattrs+2)) {
615 SV *attrname = args[argi]->attr.name;
616 SV *attrval = args[argi]->attr.value;
617
618 inplace_trim_whitespace(attrval);
619
620 mop_slot_apply_attribute(slotmeta, SvPVX(attrname), attrval);
621
622 if(attrval)
623 SvREFCNT_dec(attrval);
624
625 argi++;
626 }
627 }
628
629 /* It would be nice to just yield some OP to represent the has slot here
630 * and let normal parsing of normal scalar assignment accept it. But we can't
631 * because scalar assignment tries to peephole far too deply into us and
632 * everything breaks... :/
633 */
634 switch(args[argi++]->i) {
635 case -1:
636 /* no expr */
637 break;
638
639 case 0:
640 {
641 OP *op = args[argi++]->op;
642
643 SV *defaultsv = newSV(0);
644 mop_slot_set_default_sv(slotmeta, defaultsv);
645
646 /* An OP_CONST whose op_type is OP_CUSTOM.
647 * This way we avoid the opchecker and finalizer doing bad things to our
648 * defaultsv SV by setting it SvREADONLY_on().
649 */
650 OP *slotop = newSVOP_CUSTOM(PL_ppaddr[OP_CONST], 0, SvREFCNT_inc(defaultsv));
651
652 OP *lhs, *rhs;
653
654 switch(sigil) {
655 case '$':
656 *out = newBINOP(OP_SASSIGN, 0, op_contextualize(op, G_SCALAR), slotop);
657 break;
658
659 case '@':
660 sv_setrv_noinc(defaultsv, (SV *)newAV());
661 lhs = newUNOP(OP_RV2AV, OPf_MOD|OPf_REF, slotop);
662 goto slot_array_hash_common;
663
664 case '%':
665 sv_setrv_noinc(defaultsv, (SV *)newHV());
666 lhs = newUNOP(OP_RV2HV, OPf_MOD|OPf_REF, slotop);
667 goto slot_array_hash_common;
668
669 slot_array_hash_common:
670 rhs = op_contextualize(op, G_LIST);
671 *out = newBINOP(OP_AASSIGN, 0,
672 force_list_keeping_pushmark(rhs),
673 force_list_keeping_pushmark(lhs));
674 break;
675 }
676 }
677 break;
678
679 case 1:
680 {
681 OP *op = args[argi++]->op;
682 U8 want = 0;
683
684 forbid_outofblock_ops(op, "a slot initialiser block");
685
686 switch(sigil) {
687 case '$':
688 want = G_SCALAR;
689 break;
690 case '@':
691 case '%':
692 want = G_LIST;
693 break;
694 }
695
696 slotmeta->defaultexpr = op_contextualize(op_scope(op), want);
697 }
698 break;
699 }
700
701 mop_slot_seal(slotmeta);
702
703 return KEYWORD_PLUGIN_STMT;
704 }
705
setup_parse_has_initexpr(pTHX_ void * hookdata)706 static void setup_parse_has_initexpr(pTHX_ void *hookdata)
707 {
708 CV *was_compcv = PL_compcv;
709
710 resume_compcv_and_save(&compclassmeta->initslots_compcv);
711
712 /* Set up this new block as if the current compiler context were its scope */
713
714 if(CvOUTSIDE(PL_compcv))
715 SvREFCNT_dec(CvOUTSIDE(PL_compcv));
716
717 CvOUTSIDE(PL_compcv) = (CV *)SvREFCNT_inc(was_compcv);
718 CvOUTSIDE_SEQ(PL_compcv) = PL_cop_seqmax;
719 }
720
721 static const struct XSParseKeywordHooks kwhooks_has = {
722 .flags = XPK_FLAG_STMT|XPK_FLAG_AUTOSEMI,
723 .permit_hintkey = "Object::Pad/has",
724
725 .check = &check_has,
726
727 .pieces = (const struct XSParseKeywordPieceType []){
728 XPK_LEXVARNAME(XPK_LEXVAR_ANY),
729 XPK_ATTRIBUTES,
730 XPK_CHOICE(
731 XPK_SEQUENCE(XPK_EQUALS, XPK_TERMEXPR),
732 XPK_PREFIXED_BLOCK_ENTERLEAVE(XPK_SETUP(&setup_parse_has_initexpr)),
733 {0}
734 ),
735 {0}
736 },
737 .build = &build_has,
738 };
739
740 /* We use the method-like keyword parser to parse phaser blocks as well as
741 * methods. In order to tell what is going on, hookdata will be an integer
742 * set to one of the following
743 */
744
745 enum PhaserType {
746 PHASER_NONE, /* A normal `method`; i.e. not a phaser */
747 PHASER_BUILD,
748 PHASER_ADJUST,
749 PHASER_ADJUSTPARAMS,
750 };
751
752 static const char *phasertypename[] = {
753 [PHASER_BUILD] = "BUILD",
754 [PHASER_ADJUST] = "ADJUST",
755 [PHASER_ADJUSTPARAMS] = "ADJUSTPARAMS",
756 };
757
parse_permit(pTHX_ void * hookdata)758 static bool parse_permit(pTHX_ void *hookdata)
759 {
760 if(!have_compclassmeta)
761 croak("Cannot 'method' outside of 'class'");
762
763 return true;
764 }
765
parse_pre_subparse(pTHX_ struct XSParseSublikeContext * ctx,void * hookdata)766 static void parse_pre_subparse(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
767 {
768 enum PhaserType type = PTR2UV(hookdata);
769 U32 i;
770 AV *slots = compclassmeta->direct_slots;
771 U32 nslots = av_count(slots);
772
773 switch(type) {
774 case PHASER_NONE:
775 if(ctx->name && strEQ(SvPVX(ctx->name), "BUILD"))
776 croak("method BUILD is no longer supported; use a BUILD block instead");
777 break;
778
779 case PHASER_BUILD:
780 case PHASER_ADJUST:
781 case PHASER_ADJUSTPARAMS:
782 break;
783 }
784
785 if(type != PHASER_NONE)
786 /* We need to fool start_subparse() into thinking this is a named function
787 * so it emits a real CV and not a protosub
788 */
789 ctx->name = newSVpvs("(phaser)");
790
791 /* Save the methodscope for this subparse, in case of nested methods
792 * (RT132321)
793 */
794 SAVESPTR(compclassmeta->methodscope);
795
796 /* While creating the new scope CV we need to ENTER a block so as not to
797 * break any interpvars
798 */
799 ENTER;
800 SAVESPTR(PL_comppad);
801 SAVESPTR(PL_comppad_name);
802 SAVESPTR(PL_curpad);
803
804 CV *methodscope = compclassmeta->methodscope = MUTABLE_CV(newSV_type(SVt_PVCV));
805 CvPADLIST(methodscope) = pad_new(padnew_SAVE);
806
807 PL_comppad = PadlistARRAY(CvPADLIST(methodscope))[1];
808 PL_comppad_name = PadlistNAMES(CvPADLIST(methodscope));
809 PL_curpad = AvARRAY(PL_comppad);
810
811 for(i = 0; i < nslots; i++) {
812 SlotMeta *slotmeta = (SlotMeta *)AvARRAY(slots)[i];
813
814 /* Skip the anonymous ones */
815 if(SvCUR(slotmeta->name) < 2)
816 continue;
817
818 /* Claim these are all STATE variables just to quiet the "will not stay
819 * shared" warning */
820 pad_add_name_sv(slotmeta->name, padadd_STATE, NULL, NULL);
821 }
822
823 intro_my();
824
825 LEAVE;
826 }
827
parse_filter_attr(pTHX_ struct XSParseSublikeContext * ctx,SV * attr,SV * val,void * hookdata)828 static bool parse_filter_attr(pTHX_ struct XSParseSublikeContext *ctx, SV *attr, SV *val, void *hookdata)
829 {
830 struct AttributeDefinition *def;
831 for(def = method_attributes; def->attrname; def++) {
832 if(!strEQ(SvPVX(attr), def->attrname))
833 continue;
834
835 /* TODO: We might want to wrap the CV in some sort of MethodMeta struct
836 * but for now we'll just pass the XSParseSublikeContext context */
837 (*def->apply)(aTHX_ ctx, SvPOK(val) ? SvPVX(val) : NULL, def->applydata);
838
839 return true;
840 }
841
842 /* No error, just let it fall back to usual attribute handling */
843 return false;
844 }
845
parse_post_blockstart(pTHX_ struct XSParseSublikeContext * ctx,void * hookdata)846 static void parse_post_blockstart(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
847 {
848 /* Splice in the slot scope CV in */
849 CV *methodscope = compclassmeta->methodscope;
850
851 if(CvANON(PL_compcv))
852 CvANON_on(methodscope);
853
854 CvOUTSIDE (methodscope) = CvOUTSIDE (PL_compcv);
855 CvOUTSIDE_SEQ(methodscope) = CvOUTSIDE_SEQ(PL_compcv);
856
857 CvOUTSIDE(PL_compcv) = methodscope;
858
859 extend_pad_vars(compclassmeta);
860
861 if(compclassmeta->type == METATYPE_ROLE) {
862 PAD *pad1 = PadlistARRAY(CvPADLIST(PL_compcv))[1];
863
864 if(compclassmeta->role_is_invokable) {
865 SV *sv = PadARRAY(pad1)[PADIX_EMBEDDING];
866 sv_setpvn(sv, "", 0);
867 SvPVX(sv) = (void *)&embedding_standalone;
868 }
869 else {
870 SvREFCNT_dec(PadARRAY(pad1)[PADIX_EMBEDDING]);
871 PadARRAY(pad1)[PADIX_EMBEDDING] = &PL_sv_undef;
872 }
873 }
874
875 intro_my();
876 }
877
parse_pre_blockend(pTHX_ struct XSParseSublikeContext * ctx,void * hookdata)878 static void parse_pre_blockend(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
879 {
880 enum PhaserType type = PTR2UV(hookdata);
881 PADNAMELIST *slotnames = PadlistNAMES(CvPADLIST(compclassmeta->methodscope));
882 I32 nslots = av_count(compclassmeta->direct_slots);
883 PADNAME **snames = PadnamelistARRAY(slotnames);
884 PADNAME **padnames = PadnamelistARRAY(PadlistNAMES(CvPADLIST(PL_compcv)));
885 OP *slotops = NULL;
886
887 #if HAVE_PERL_VERSION(5, 22, 0)
888 U32 cop_seq_low = COP_SEQ_RANGE_LOW(padnames[PADIX_SELF]);
889 #endif
890
891 {
892 ENTER;
893 SAVEVPTR(PL_curcop);
894
895 /* See https://rt.cpan.org/Ticket/Display.html?id=132428
896 * https://github.com/Perl/perl5/issues/17754
897 */
898 PADOFFSET padix;
899 for(padix = PADIX_SELF + 1; padix <= PadnamelistMAX(PadlistNAMES(CvPADLIST(PL_compcv))); padix++) {
900 PADNAME *pn = padnames[padix];
901
902 if(PadnameIsNULL(pn) || !PadnameLEN(pn))
903 continue;
904
905 const char *pv = PadnamePV(pn);
906 if(!pv || !strEQ(pv, "$self"))
907 continue;
908
909 COP *padcop = NULL;
910 if(find_cop_for_lvintro(padix, ctx->body, &padcop))
911 PL_curcop = padcop;
912 warn("\"my\" variable $self masks earlier declaration in same scope");
913 }
914
915 LEAVE;
916 }
917
918 slotops = op_append_list(OP_LINESEQ, slotops,
919 newSTATEOP(0, NULL, NULL));
920 slotops = op_append_list(OP_LINESEQ, slotops,
921 newMETHSTARTOP(0 |
922 (compclassmeta->type == METATYPE_ROLE ? OPf_SPECIAL : 0) |
923 (compclassmeta->repr << 8)));
924
925 int i;
926 for(i = 0; i < nslots; i++) {
927 SlotMeta *slotmeta = (SlotMeta *)AvARRAY(compclassmeta->direct_slots)[i];
928 PADNAME *slotname = snames[i + 1];
929
930 if(!slotname
931 #if HAVE_PERL_VERSION(5, 22, 0)
932 /* On perl 5.22 and above we can use PadnameREFCNT to detect which pad
933 * slots are actually being used
934 */
935 || PadnameREFCNT(slotname) < 2
936 #endif
937 )
938 continue;
939
940 SLOTOFFSET slotix = slotmeta->slotix;
941 PADOFFSET padix = find_padix_for_slot(slotmeta);
942
943 if(padix == NOT_IN_PAD)
944 continue;
945
946 U8 private = 0;
947 switch(SvPV_nolen(slotmeta->name)[0]) {
948 case '$': private = OPpSLOTPAD_SV; break;
949 case '@': private = OPpSLOTPAD_AV; break;
950 case '%': private = OPpSLOTPAD_HV; break;
951 }
952
953 slotops = op_append_list(OP_LINESEQ, slotops,
954 /* alias the padix from the slot */
955 newSLOTPADOP(private << 8, padix, slotix));
956
957 #if HAVE_PERL_VERSION(5, 22, 0)
958 /* Unshare the padname so the one in the scopeslot returns to refcount 1 */
959 PADNAME *newpadname = newPADNAMEpvn(PadnamePV(slotname), PadnameLEN(slotname));
960 PadnameREFCNT_dec(padnames[padix]);
961 padnames[padix] = newpadname;
962
963 /* Turn off OUTER and set a valid COP sequence range, so the lexical is
964 * visible to eval(), PadWalker, perldb, etc.. */
965 PadnameOUTER_off(newpadname);
966 COP_SEQ_RANGE_LOW(newpadname) = cop_seq_low;
967 COP_SEQ_RANGE_HIGH(newpadname) = PL_cop_seqmax;
968 #endif
969 }
970
971 ctx->body = op_append_list(OP_LINESEQ, slotops, ctx->body);
972
973 compclassmeta->methodscope = NULL;
974
975 /* Restore CvOUTSIDE(PL_compcv) back to where it should be */
976 {
977 CV *outside = CvOUTSIDE(PL_compcv);
978 PADNAMELIST *pnl = PadlistNAMES(CvPADLIST(PL_compcv));
979 PADNAMELIST *outside_pnl = PadlistNAMES(CvPADLIST(outside));
980
981 /* Lexical captures will need their parent pad index fixing
982 * Technically these only matter for CvANON because they're only used when
983 * reconstructing the parent pad captures by OP_ANONCODE. But we might as
984 * well be polite and fix them for all CVs
985 */
986 PADOFFSET padix;
987 for(padix = 1; padix <= PadnamelistMAX(pnl); padix++) {
988 PADNAME *pn = PadnamelistARRAY(pnl)[padix];
989 if(PadnameIsNULL(pn) ||
990 !PadnameOUTER(pn) ||
991 !PARENT_PAD_INDEX(pn))
992 continue;
993
994 PADNAME *outside_pn = PadnamelistARRAY(outside_pnl)[PARENT_PAD_INDEX(pn)];
995
996 PARENT_PAD_INDEX_set(pn, PARENT_PAD_INDEX(outside_pn));
997 if(!PadnameOUTER(outside_pn))
998 PadnameOUTER_off(pn);
999 }
1000
1001 CvOUTSIDE(PL_compcv) = CvOUTSIDE(outside);
1002 CvOUTSIDE_SEQ(PL_compcv) = CvOUTSIDE_SEQ(outside);
1003 }
1004
1005 if(type != PHASER_NONE) {
1006 /* We need to remove the name now to stop newATTRSUB() from creating this
1007 * as a named symbol table entry
1008 */
1009 SvREFCNT_dec(ctx->name);
1010 ctx->name = NULL;
1011 }
1012 }
1013
parse_post_newcv(pTHX_ struct XSParseSublikeContext * ctx,void * hookdata)1014 static void parse_post_newcv(pTHX_ struct XSParseSublikeContext *ctx, void *hookdata)
1015 {
1016 enum PhaserType type = PTR2UV(hookdata);
1017
1018 if(ctx->cv)
1019 CvMETHOD_on(ctx->cv);
1020
1021 switch(type) {
1022 case PHASER_NONE:
1023 if(ctx->cv && ctx->name)
1024 mop_class_add_method(compclassmeta, ctx->name);
1025 break;
1026
1027 case PHASER_BUILD:
1028 mop_class_add_BUILD(compclassmeta, ctx->cv); /* steal CV */
1029 break;
1030
1031 case PHASER_ADJUST:
1032 mop_class_add_ADJUST(compclassmeta, ctx->cv); /* steal CV */
1033 break;
1034
1035 case PHASER_ADJUSTPARAMS:
1036 mop_class_add_ADJUSTPARAMS(compclassmeta, ctx->cv); /* steal CV */
1037 break;
1038 }
1039
1040 /* Any phaser should parse as if it was a named method. By setting a junk
1041 * name here we fool XS::Parse::Sublike into thinking it just parsed a named
1042 * method, so it emits an OP_NULL into the optree and behaves like a
1043 * statement
1044 */
1045 if(type != PHASER_NONE)
1046 ctx->name = newSVpvs("(phaser)");
1047 }
1048
1049 static struct XSParseSublikeHooks parse_method_hooks = {
1050 .flags = XS_PARSE_SUBLIKE_FLAG_FILTERATTRS,
1051 .permit_hintkey = "Object::Pad/method",
1052 .permit = parse_permit,
1053 .pre_subparse = parse_pre_subparse,
1054 .filter_attr = parse_filter_attr,
1055 .post_blockstart = parse_post_blockstart,
1056 .pre_blockend = parse_pre_blockend,
1057 .post_newcv = parse_post_newcv,
1058 };
1059
1060 static struct XSParseSublikeHooks parse_phaser_hooks = {
1061 .skip_parts = XS_PARSE_SUBLIKE_PART_NAME|XS_PARSE_SUBLIKE_PART_ATTRS,
1062 /* no permit */
1063 .pre_subparse = parse_pre_subparse,
1064 .post_blockstart = parse_post_blockstart,
1065 .pre_blockend = parse_pre_blockend,
1066 .post_newcv = parse_post_newcv,
1067 };
1068
parse_phaser(pTHX_ OP ** out,void * hookdata)1069 static int parse_phaser(pTHX_ OP **out, void *hookdata)
1070 {
1071 if(!have_compclassmeta)
1072 croak("Cannot '%s' outside of 'class'", phasertypename[PTR2UV(hookdata)]);
1073
1074 lex_read_space(0);
1075
1076 return xs_parse_sublike(&parse_phaser_hooks, hookdata, out);
1077 }
1078
1079 static const struct XSParseKeywordHooks kwhooks_phaser = {
1080 .permit_hintkey = "Object::Pad/method",
1081 .parse = &parse_phaser,
1082 };
1083
check_requires(pTHX_ void * hookdata)1084 static void check_requires(pTHX_ void *hookdata)
1085 {
1086 if(!have_compclassmeta)
1087 croak("Cannot 'requires' outside of 'role'");
1088
1089 if(compclassmeta->type == METATYPE_CLASS)
1090 croak("A class may not declare required methods");
1091 }
1092
build_requires(pTHX_ OP ** out,XSParseKeywordPiece * args[],size_t nargs,void * hookdata)1093 static int build_requires(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
1094 {
1095 SV *mname = args[0]->sv;
1096
1097 av_push(compclassmeta->requiremethods, mname);
1098
1099 *out = newOP(OP_NULL, 0);
1100
1101 return KEYWORD_PLUGIN_STMT;
1102 }
1103
1104 static const struct XSParseKeywordHooks kwhooks_requires = {
1105 .flags = XPK_FLAG_STMT|XPK_FLAG_AUTOSEMI,
1106 .permit_hintkey = "Object::Pad/requires",
1107
1108 .check = &check_requires,
1109
1110 .pieces = (const struct XSParseKeywordPieceType []){
1111 XPK_IDENT,
1112 {0}
1113 },
1114 .build = &build_requires,
1115 };
1116
1117 #ifdef HAVE_DMD_HELPER
dump_slotmeta(pTHX_ const SV * sv,SlotMeta * slotmeta)1118 static int dump_slotmeta(pTHX_ const SV *sv, SlotMeta *slotmeta)
1119 {
1120 int ret = 0;
1121
1122 /* Some trickery to generate dynamic labels */
1123 const char *name = SvPVX(slotmeta->name);
1124 SV *label = newSV(0);
1125
1126 sv_setpvf(label, "the Object::Pad slot %s name", name);
1127 ret += DMD_ANNOTATE_SV(sv, slotmeta->name, SvPVX(label));
1128
1129 sv_setpvf(label, "the Object::Pad slot %s default value", name);
1130 ret += DMD_ANNOTATE_SV(sv, mop_slot_get_default_sv(slotmeta), SvPVX(label));
1131
1132 SvREFCNT_dec(label);
1133
1134 return ret;
1135 }
1136
dumppackage_class(pTHX_ const SV * sv)1137 static int dumppackage_class(pTHX_ const SV *sv)
1138 {
1139 int ret = 0;
1140 ClassMeta *meta = NUM2PTR(ClassMeta *, SvUV((SV *)sv));
1141
1142 ret += DMD_ANNOTATE_SV(sv, meta->name, "the Object::Pad class name");
1143 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->stash, "the Object::Pad stash");
1144 if(meta->pending_submeta)
1145 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->pending_submeta, "the Object::Pad pending submeta AV");
1146
1147 I32 i;
1148 for(i = 0; i < av_count(meta->direct_slots); i++)
1149 ret += dump_slotmeta(aTHX_ sv, (SlotMeta *)AvARRAY(meta->direct_slots)[i]);
1150
1151 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->initslots, "the Object::Pad initslots CV");
1152
1153 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->buildblocks, "the Object::Pad BUILD blocks AV");
1154
1155 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->adjustblocks, "the Object::Pad ADJUST blocks AV");
1156
1157 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->methodscope, "the Object::Pad temporary method scope");
1158
1159 switch(meta->type) {
1160 case METATYPE_CLASS:
1161 if(meta->cls.foreign_new)
1162 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->cls.foreign_new, "the Object::Pad foreign superclass constructor CV");
1163 if(meta->cls.direct_roles)
1164 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->cls.direct_roles, "the Object::Pad direct roles AV");
1165 break;
1166
1167 case METATYPE_ROLE:
1168 ret += DMD_ANNOTATE_SV(sv, (SV *)meta->role.applied_classes, "the Object::Pad role applied classes HV");
1169 break;
1170 }
1171
1172 return ret;
1173 }
1174 #endif
1175
1176 /********************
1177 * Custom SlotHooks *
1178 ********************/
1179
1180 struct CustomSlotHookData
1181 {
1182 SV *apply_cb;
1183 };
1184
slothook_custom_apply(pTHX_ SlotMeta * slotmeta,SV * value,SV ** hookdata_ptr,void * _funcdata)1185 static bool slothook_custom_apply(pTHX_ SlotMeta *slotmeta, SV *value, SV **hookdata_ptr, void *_funcdata)
1186 {
1187 struct CustomSlotHookData *funcdata = _funcdata;
1188
1189 SV *cb;
1190 if((cb = funcdata->apply_cb)) {
1191 dSP;
1192 ENTER;
1193 SAVETMPS;
1194
1195 SV *slotmetasv = sv_newmortal();
1196 sv_setref_uv(slotmetasv, "Object::Pad::MOP::Slot", PTR2UV(slotmeta));
1197
1198 PUSHMARK(SP);
1199 EXTEND(SP, 2);
1200 PUSHs(slotmetasv);
1201 PUSHs(value);
1202 PUTBACK;
1203
1204 call_sv(cb, G_SCALAR);
1205
1206 SPAGAIN;
1207 SV *ret = POPs;
1208 *hookdata_ptr = SvREFCNT_inc(ret);
1209
1210 FREETMPS;
1211 LEAVE;
1212 }
1213
1214 return TRUE;
1215 }
1216
1217 MODULE = Object::Pad PACKAGE = Object::Pad::MOP::Class
1218
1219 INCLUDE: mop-class.xsi
1220
1221 MODULE = Object::Pad PACKAGE = Object::Pad::MOP::Method
1222
1223 INCLUDE: mop-method.xsi
1224
1225 MODULE = Object::Pad PACKAGE = Object::Pad::MOP::Slot
1226
1227 INCLUDE: mop-slot.xsi
1228
1229 MODULE = Object::Pad PACKAGE = Object::Pad::MOP::SlotAttr
1230
1231 void
1232 register(class, name, ...)
1233 SV *class
1234 SV *name
1235 CODE:
1236 {
1237 PERL_UNUSED_VAR(class);
1238 dKWARG(2);
1239
1240 struct SlotHookFuncs *funcs;
1241 Newxz(funcs, 1, struct SlotHookFuncs);
1242
1243 struct CustomSlotHookData *funcdata;
1244 Newxz(funcdata, 1, struct CustomSlotHookData);
1245
1246 funcs->ver = OBJECTPAD_ABIVERSION;
1247
1248 funcs->apply = &slothook_custom_apply;
1249
1250 static const char *args[] = {
1251 "permit_hintkey",
1252 "apply",
1253 NULL,
1254 };
1255 while(KWARG_NEXT(args)) {
1256 switch(kwarg) {
1257 case 0: /* permit_hintkey */
1258 funcs->permit_hintkey = savepv(SvPV_nolen(kwval));
1259 break;
1260
1261 case 1: /* apply */
1262 funcdata->apply_cb = newSVsv(kwval);
1263 break;
1264 }
1265 }
1266
1267 register_slot_attribute(savepv(SvPV_nolen(name)), funcs, funcdata);
1268 }
1269
1270 BOOT:
1271 XopENTRY_set(&xop_methstart, xop_name, "methstart");
1272 XopENTRY_set(&xop_methstart, xop_desc, "methstart()");
1273 XopENTRY_set(&xop_methstart, xop_class, OA_BASEOP);
1274 Perl_custom_op_register(aTHX_ &pp_methstart, &xop_methstart);
1275
1276 XopENTRY_set(&xop_slotpad, xop_name, "slotpad");
1277 XopENTRY_set(&xop_slotpad, xop_desc, "slotpad()");
1278 #ifdef HAVE_UNOP_AUX
1279 XopENTRY_set(&xop_slotpad, xop_class, OA_UNOP_AUX);
1280 #else
1281 XopENTRY_set(&xop_slotpad, xop_class, OA_UNOP); /* technically a lie */
1282 #endif
1283 Perl_custom_op_register(aTHX_ &pp_slotpad, &xop_slotpad);
1284
1285 CvLVALUE_on(get_cv("Object::Pad::MOP::Slot::value", 0));
1286 #ifdef HAVE_DMD_HELPER
1287 DMD_SET_PACKAGE_HELPER("Object::Pad::MOP::Class", &dumppackage_class);
1288 #endif
1289
1290 boot_xs_parse_keyword(0.10); /* XPK_OPTIONAL(XPK_CHOICE...) */
1291
1292 register_xs_parse_keyword("class", &kwhooks_class, (void *)METATYPE_CLASS);
1293 register_xs_parse_keyword("role", &kwhooks_role, (void *)METATYPE_ROLE);
1294
1295 register_xs_parse_keyword("has", &kwhooks_has, NULL);
1296
1297 register_xs_parse_keyword("BUILD", &kwhooks_phaser, (void *)PHASER_BUILD);
1298 register_xs_parse_keyword("ADJUST", &kwhooks_phaser, (void *)PHASER_ADJUST);
1299 register_xs_parse_keyword("ADJUSTPARAMS", &kwhooks_phaser, (void *)PHASER_ADJUSTPARAMS);
1300
1301 register_xs_parse_keyword("requires", &kwhooks_requires, NULL);
1302
1303 boot_xs_parse_sublike(0.13); /* permit_hintkey */
1304
1305 register_xs_parse_sublike("method", &parse_method_hooks, (void *)PHASER_NONE);
1306
1307 ObjectPad__boot_classes();
1308 ObjectPad__boot_slots(aTHX);
1309