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