]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
Fix the thread destructor trick for 5.13.1
[perl/modules/re-engine-Plugin.git] / Plugin.xs
1 /* This file is part of the re::engine::Plugin Perl module.
2  * See http://search.cpan.org/dist/re-engine-Plugin/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #include "Plugin.h"
10
11 #define __PACKAGE__     "re::engine::Plugin"
12 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
13
14 #define REP_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
15
16 #undef ENTERn
17 #if defined(ENTER_with_name) && !REP_HAS_PERL(5, 11, 4)
18 # define ENTERn(N) ENTER_with_name(N)
19 #else
20 # define ENTERn(N) ENTER
21 #endif
22
23 #undef LEAVEn
24 #if defined(LEAVE_with_name) && !REP_HAS_PERL(5, 11, 4)
25 # define LEAVEn(N) LEAVE_with_name(N)
26 #else
27 # define LEAVEn(N) LEAVE
28 #endif
29
30 #ifndef REP_WORKAROUND_REQUIRE_PROPAGATION
31 # define REP_WORKAROUND_REQUIRE_PROPAGATION !REP_HAS_PERL(5, 10, 1)
32 #endif
33
34 /* ... Thread safety and multiplicity ...................................... */
35
36 #ifndef REP_MULTIPLICITY
37 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
38 #  define REP_MULTIPLICITY 1
39 # else
40 #  define REP_MULTIPLICITY 0
41 # endif
42 #endif
43 #if REP_MULTIPLICITY && !defined(tTHX)
44 # define tTHX PerlInterpreter*
45 #endif
46
47 #if REP_MULTIPLICITY && defined(USE_ITHREADS) && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && (defined(MY_CXT_CLONE) || defined(dMY_CXT_SV))
48 # define REP_THREADSAFE 1
49 # ifndef MY_CXT_CLONE
50 #  define MY_CXT_CLONE \
51     dMY_CXT_SV;                                                      \
52     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
53     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
54     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
55 # endif
56 #else
57 # define REP_THREADSAFE 0
58 # undef  dMY_CXT
59 # define dMY_CXT      dNOOP
60 # undef  MY_CXT
61 # define MY_CXT       rep_globaldata
62 # undef  START_MY_CXT
63 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
64 # undef  MY_CXT_INIT
65 # define MY_CXT_INIT  NOOP
66 # undef  MY_CXT_CLONE
67 # define MY_CXT_CLONE NOOP
68 #endif
69
70 /* --- Helpers ------------------------------------------------------------- */
71
72 /* ... Thread-safe hints ................................................... */
73
74 typedef struct {
75  SV *comp;
76  SV *exec;
77 #if REP_WORKAROUND_REQUIRE_PROPAGATION
78  IV  cxreq;
79 #endif
80 } rep_hint_t;
81
82 #if REP_THREADSAFE
83
84 #define PTABLE_VAL_FREE(V) { \
85  rep_hint_t *h = (V);        \
86  SvREFCNT_dec(h->comp);      \
87  SvREFCNT_dec(h->exec);      \
88  PerlMemShared_free(h);      \
89 }
90
91 #define pPTBL  pTHX
92 #define pPTBL_ pTHX_
93 #define aPTBL  aTHX
94 #define aPTBL_ aTHX_
95
96 #include "ptable.h"
97
98 #define ptable_store(T, K, V) ptable_store(aTHX_ (T), (K), (V))
99 #define ptable_free(T)        ptable_free(aTHX_ (T))
100
101 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
102
103 typedef struct {
104  ptable *tbl;
105  tTHX    owner;
106 } my_cxt_t;
107
108 START_MY_CXT
109
110 STATIC SV *rep_clone(pTHX_ SV *sv, tTHX owner) {
111 #define rep_clone(S, O) rep_clone(aTHX_ (S), (O))
112  CLONE_PARAMS  param;
113  AV           *stashes = NULL;
114  SV           *dupsv;
115
116  if (SvTYPE(sv) == SVt_PVHV && HvNAME_get(sv))
117   stashes = newAV();
118
119  param.stashes    = stashes;
120  param.flags      = 0;
121  param.proto_perl = owner;
122
123  dupsv = sv_dup(sv, &param);
124
125  if (stashes) {
126   av_undef(stashes);
127   SvREFCNT_dec(stashes);
128  }
129
130  return SvREFCNT_inc(dupsv);
131 }
132
133 STATIC void rep_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) {
134  my_cxt_t   *ud = ud_;
135  rep_hint_t *h1 = ent->val;
136  rep_hint_t *h2;
137
138  if (ud->owner == aTHX)
139   return;
140
141  h2        = PerlMemShared_malloc(sizeof *h2);
142  h2->comp  = rep_clone(h1->comp, ud->owner);
143  SvREFCNT_inc(h2->comp);
144  h2->exec  = rep_clone(h1->exec, ud->owner);
145  SvREFCNT_inc(h2->exec);
146 #if REP_WORKAROUND_REQUIRE_PROPAGATION
147  h2->cxreq = h1->cxreq;
148 #endif
149
150  ptable_store(ud->tbl, ent->key, h2);
151 }
152
153 #include "reap.h"
154
155 STATIC void rep_thread_cleanup(pTHX_ void *ud) {
156  dMY_CXT;
157
158  ptable_free(MY_CXT.tbl);
159 }
160
161 #endif /* REP_THREADSAFE */
162
163 STATIC SV *rep_validate_callback(SV *code) {
164  if (!SvROK(code))
165   return NULL;
166
167  code = SvRV(code);
168  if (SvTYPE(code) < SVt_PVCV)
169   return NULL;
170
171  return SvREFCNT_inc_simple_NN(code);
172 }
173
174 #if REP_WORKAROUND_REQUIRE_PROPAGATION
175 STATIC IV rep_require_tag(pTHX) {
176 #define rep_require_tag() rep_require_tag(aTHX)
177  const PERL_SI *si;
178
179  for (si = PL_curstackinfo; si; si = si->si_prev) {
180   I32 cxix;
181
182   for (cxix = si->si_cxix; cxix >= 0; --cxix) {
183    const PERL_CONTEXT *cx = si->si_cxstack + cxix;
184
185    if (CxTYPE(cx) == CXt_EVAL && cx->blk_eval.old_op_type == OP_REQUIRE)
186     return PTR2IV(cx);
187   }
188  }
189
190  return PTR2IV(NULL);
191 }
192 #endif /* REP_WORKAROUND_REQUIRE_PROPAGATION */
193
194 STATIC SV *rep_tag(pTHX_ SV *comp, SV *exec) {
195 #define rep_tag(C, E) rep_tag(aTHX_ (C), (E))
196  rep_hint_t *h;
197  dMY_CXT;
198
199  h = PerlMemShared_malloc(sizeof *h);
200  h->comp  = rep_validate_callback(comp);
201  h->exec  = rep_validate_callback(exec);
202 #if REP_WORKAROUND_REQUIRE_PROPAGATION
203  h->cxreq = rep_require_tag();
204 #endif /* REP_WORKAROUND_REQUIRE_PROPAGATION */
205
206 #if REP_THREADSAFE
207  /* We only need for the key to be an unique tag for looking up the value later.
208   * Allocated memory provides convenient unique identifiers, so that's why we
209   * use the hint as the key itself. */
210  ptable_store(MY_CXT.tbl, h, h);
211 #endif /* REP_THREADSAFE */
212
213  return newSViv(PTR2IV(h));
214 }
215
216 STATIC const rep_hint_t *rep_detag(pTHX_ const SV *hint) {
217 #define rep_detag(H) rep_detag(aTHX_ (H))
218  rep_hint_t *h;
219  dMY_CXT;
220
221  if (!(hint && SvIOK(hint)))
222   return NULL;
223
224  h = INT2PTR(rep_hint_t *, SvIVX(hint));
225 #if REP_THREADSAFE
226  h = ptable_fetch(MY_CXT.tbl, h);
227 #endif /* REP_THREADSAFE */
228
229 #if REP_WORKAROUND_REQUIRE_PROPAGATION
230  if (rep_require_tag() != h->cxreq)
231   return NULL;
232 #endif /* REP_WORKAROUND_REQUIRE_PROPAGATION */
233
234  return h;
235 }
236
237 STATIC U32 rep_hash = 0;
238
239 STATIC const rep_hint_t *rep_hint(pTHX) {
240 #define rep_hint() rep_hint(aTHX)
241  SV *hint;
242
243 #ifdef cop_hints_fetch_pvn
244  hint = cop_hints_fetch_pvn(PL_curcop,
245                             __PACKAGE__, __PACKAGE_LEN__, rep_hash, 0);
246 #else
247  /* We already require 5.9.5 for the regexp engine API. */
248  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
249                                        NULL,
250                                        __PACKAGE__, __PACKAGE_LEN__,
251                                        0,
252                                        rep_hash);
253 #endif
254
255  return rep_detag(hint);
256 }
257
258 REGEXP *
259 #if PERL_VERSION <= 10
260 Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
261 #else
262 Plugin_comp(pTHX_ SV * const pattern, U32 flags)
263 #endif
264 {
265     dSP;
266     struct regexp * rx;
267     REGEXP *RX;
268     I32 buffers;
269     re__engine__Plugin re;
270     const rep_hint_t *h;
271
272     h = rep_hint();
273     if (!h) /* This looks like a pragma leak. Apply the default behaviour */
274         return re_compile(pattern, flags);
275
276     /* exp/xend version of the pattern & length */
277     STRLEN plen;
278     char*  exp = SvPV((SV*)pattern, plen);
279
280     /* Our blessed object */
281     SV *obj = newSV(0);
282     SvREFCNT_inc(obj);
283     Newxz(re, 1, struct replug);
284     sv_setref_pv(obj, "re::engine::Plugin", (void*)re);
285
286     newREGEXP(RX);
287     rx = rxREGEXP(RX);
288
289     re->rx = rx;                   /* Make the rx accessible from self->rx */
290     rx->intflags = flags;          /* Flags for internal use */
291     rx->extflags = flags;          /* Flags for perl to use */
292     rx->engine = RE_ENGINE_PLUGIN; /* Compile to use this engine */
293
294 #if PERL_VERSION <= 10
295     rx->refcnt = 1;                /* Refcount so we won't be destroyed */
296
297     /* Precompiled pattern for pp_regcomp to use */
298     rx->prelen = plen;
299     rx->precomp = savepvn(exp, rx->prelen);
300
301     /* Set up qr// stringification to be equivalent to the supplied
302      * pattern, this should be done via overload eventually.
303      */
304     rx->wraplen = rx->prelen;
305     Newx(rx->wrapped, rx->wraplen, char);
306     Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
307 #endif
308
309     /* Store our private object */
310     rx->pprivate = obj;
311
312     /* Store the pattern for ->pattern */
313     re->pattern = (SV*)pattern;
314     SvREFCNT_inc(re->pattern);
315
316     /* If there's an exec callback, store it into the private object so
317      * that it will be the one to be called, even if the engine changes
318      * in between */
319     if (h->exec) {
320         re->cb_exec = h->exec;
321         SvREFCNT_inc_simple_void_NN(h->exec);
322     }
323
324     re->cb_num_capture_buff_FETCH  = NULL;
325     re->cb_num_capture_buff_STORE  = NULL;
326     re->cb_num_capture_buff_LENGTH = NULL;
327
328     /* Call our callback function if one was defined, if not we've
329      * already set up all the stuff we're going to to need for
330      * subsequent exec and other calls */
331     if (h->comp) {
332         ENTER;    
333         SAVETMPS;
334    
335         PUSHMARK(SP);
336         XPUSHs(obj);
337         PUTBACK;
338
339         call_sv(h->comp, G_DISCARD);
340
341         FREETMPS;
342         LEAVE;
343     }
344
345     /* If any of the comp-time accessors were called we'll have to
346      * update the regexp struct with the new info.
347      */
348
349     buffers = rx->nparens;
350
351     Newxz(rx->offs, buffers + 1, regexp_paren_pair);
352
353     return RX;
354 }
355
356 I32
357 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
358             char *strbeg, I32 minend, SV *sv, void *data, U32 flags)
359 {
360     dSP;
361     I32 matched;
362     struct regexp *rx = rxREGEXP(RX);
363     GET_SELF_FROM_PPRIVATE(rx->pprivate);
364
365     if (self->cb_exec) {
366         /* Store the current str for ->str */
367         self->str = (SV*)sv;
368         SvREFCNT_inc(self->str);
369
370         ENTER;
371         SAVETMPS;
372    
373         PUSHMARK(SP);
374         XPUSHs(rx->pprivate);
375         XPUSHs(sv);
376         PUTBACK;
377
378         call_sv(self->cb_exec, G_SCALAR);
379  
380         SPAGAIN;
381
382         SV * ret = POPs;
383
384         if (SvTRUE(ret))
385             matched = 1;
386         else
387             matched = 0;
388
389         PUTBACK;
390         FREETMPS;
391         LEAVE;
392     } else {
393         matched = 0;
394     }
395
396     return matched;
397 }
398
399 char *
400 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
401                      char *strend, U32 flags, re_scream_pos_data *data)
402 {
403     PERL_UNUSED_ARG(RX);
404     PERL_UNUSED_ARG(sv);
405     PERL_UNUSED_ARG(strpos);
406     PERL_UNUSED_ARG(strend);
407     PERL_UNUSED_ARG(flags);
408     PERL_UNUSED_ARG(data);
409     return NULL;
410 }
411
412 SV *
413 Plugin_checkstr(pTHX_ REGEXP * const RX)
414 {
415     PERL_UNUSED_ARG(RX);
416     return NULL;
417 }
418
419 void
420 Plugin_free(pTHX_ REGEXP * const RX)
421 {
422     struct regexp *rx = rxREGEXP(RX);
423     GET_SELF_FROM_PPRIVATE(rx->pprivate);
424
425     SvREFCNT_dec(self->pattern);
426     SvREFCNT_dec(self->str);
427
428     SvREFCNT_dec(self->cb_exec);
429
430     SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
431     SvREFCNT_dec(self->cb_num_capture_buff_STORE);
432     SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
433
434     self->rx = NULL;
435     Safefree(self);
436
437 /*
438     dSP;
439     SV * callback;
440
441     callback = self->cb_free;
442
443     if (callback) {
444         ENTER;
445         SAVETMPS;
446    
447         PUSHMARK(SP);
448         XPUSHs(rx->pprivate);
449         PUTBACK;
450
451         call_sv(callback, G_DISCARD);
452
453         PUTBACK;
454         FREETMPS;
455         LEAVE;
456     }
457     return;
458 */
459 }
460
461 void *
462 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
463 {
464     struct regexp *rx = rxREGEXP(RX);
465     Perl_croak(aTHX_ "dupe not supported yet");
466     return rx->pprivate;
467 }
468
469
470 void
471 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
472                            SV * const sv)
473 {
474     dSP;
475     I32 items;
476     SV * callback;
477     struct regexp *rx = rxREGEXP(RX);
478     GET_SELF_FROM_PPRIVATE(rx->pprivate);
479
480     callback = self->cb_num_capture_buff_FETCH;
481
482     if (callback) {
483         ENTER;
484         SAVETMPS;
485    
486         PUSHMARK(SP);
487         XPUSHs(rx->pprivate);
488         XPUSHs(sv_2mortal(newSViv(paren)));
489         PUTBACK;
490
491         items = call_sv(callback, G_SCALAR);
492         
493         if (items == 1) {
494             SPAGAIN;
495
496             SV * ret = POPs;
497             sv_setsv(sv, ret);
498         } else {
499             sv_setsv(sv, &PL_sv_undef);
500         }
501
502         PUTBACK;
503         FREETMPS;
504         LEAVE;
505     } else {
506         sv_setsv(sv, &PL_sv_undef);
507     }
508 }
509
510 void
511 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
512                            SV const * const value)
513 {
514     dSP;
515     SV * callback;
516     struct regexp *rx = rxREGEXP(RX);
517     GET_SELF_FROM_PPRIVATE(rx->pprivate);
518
519     callback = self->cb_num_capture_buff_STORE;
520
521     if (callback) {
522         ENTER;
523         SAVETMPS;
524    
525         PUSHMARK(SP);
526         XPUSHs(rx->pprivate);
527         XPUSHs(sv_2mortal(newSViv(paren)));
528         XPUSHs(SvREFCNT_inc((SV *) value));
529         PUTBACK;
530
531         call_sv(callback, G_DISCARD);
532
533         PUTBACK;
534         FREETMPS;
535         LEAVE;
536     }
537 }
538
539 I32
540 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
541                               const I32 paren)
542 {
543     dSP;
544     SV * callback;
545     struct regexp *rx = rxREGEXP(RX);
546     GET_SELF_FROM_PPRIVATE(rx->pprivate);
547
548     callback = self->cb_num_capture_buff_LENGTH;
549
550     if (callback) {
551         ENTER;
552         SAVETMPS;
553    
554         PUSHMARK(SP);
555         XPUSHs(rx->pprivate);
556         XPUSHs(sv_2mortal(newSViv(paren)));
557         PUTBACK;
558
559         call_sv(callback, G_SCALAR);
560
561         SPAGAIN;
562
563         IV ret = POPi;
564
565         PUTBACK;
566         FREETMPS;
567         LEAVE;
568
569         return (I32)ret;
570     } else {
571         /* TODO: call FETCH and get the length on that value */
572         return 0;
573     }
574 }
575
576
577 SV*
578 Plugin_named_buff (pTHX_ REGEXP * const RX, SV * const key, SV * const value,
579                    const U32 flags)
580 {
581     return NULL;
582 }
583
584 SV*
585 Plugin_named_buff_iter (pTHX_ REGEXP * const RX, const SV * const lastkey,
586                         const U32 flags)
587 {
588     return NULL;
589 }
590
591 SV*
592 Plugin_package(pTHX_ REGEXP * const RX)
593 {
594     PERL_UNUSED_ARG(RX);
595     return newSVpvs("re::engine::Plugin");
596 }
597
598 #if REP_THREADSAFE
599
600 STATIC U32 rep_initialized = 0;
601
602 STATIC void rep_teardown(pTHX_ void *root) {
603  dMY_CXT;
604
605  if (!rep_initialized || aTHX != root)
606   return;
607
608  ptable_free(MY_CXT.tbl);
609
610  rep_initialized = 0;
611 }
612
613 STATIC void rep_setup(pTHX) {
614 #define rep_setup() rep_setup(aTHX)
615  if (rep_initialized)
616   return;
617
618  MY_CXT_INIT;
619  MY_CXT.tbl   = ptable_new();
620  MY_CXT.owner = aTHX;
621
622  call_atexit(rep_teardown, aTHX);
623
624  rep_initialized = 1;
625 }
626
627 #else  /*  REP_THREADSAFE */
628
629 #define rep_setup()
630
631 #endif /* !REP_THREADSAFE */
632
633 STATIC U32 rep_booted = 0;
634
635 /* --- XS ------------------------------------------------------------------ */
636
637 MODULE = re::engine::Plugin     PACKAGE = re::engine::Plugin
638
639 PROTOTYPES: DISABLE
640
641 BOOT:
642 {
643     if (!rep_booted++) {
644         PERL_HASH(rep_hash, __PACKAGE__, __PACKAGE_LEN__);
645     }
646
647     rep_setup();
648 }
649
650 #if REP_THREADSAFE
651
652 void
653 CLONE(...)
654 PREINIT:
655     ptable *t;
656 PPCODE:
657     {
658         my_cxt_t ud;
659         dMY_CXT;
660         ud.tbl   = t = ptable_new();
661         ud.owner = MY_CXT.owner;
662         ptable_walk(MY_CXT.tbl, rep_ptable_clone, &ud);
663     }
664     {
665         MY_CXT_CLONE;
666         MY_CXT.tbl   = t;
667         MY_CXT.owner = aTHX;
668     }
669     reap(3, rep_thread_cleanup, NULL);
670     XSRETURN(0);
671
672 #endif /* REP_THREADSAFE */
673
674 void
675 pattern(re::engine::Plugin self, ...)
676 PPCODE:
677     XPUSHs(self->pattern);
678
679 void
680 str(re::engine::Plugin self, ...)
681 PPCODE:
682     XPUSHs(self->str);
683
684 char*
685 mod(re::engine::Plugin self, ...)
686 PPCODE:
687     /* /i */
688     if (self->rx->intflags & PMf_FOLD) {
689       XPUSHs(sv_2mortal(newSVpvs("i")));
690       XPUSHs(&PL_sv_yes);
691     }
692
693     /* /m */
694     if (self->rx->intflags & PMf_MULTILINE) {
695       XPUSHs(sv_2mortal(newSVpvs("m")));
696       XPUSHs(&PL_sv_yes);
697     }
698
699     /* /s */
700     if (self->rx->intflags & PMf_SINGLELINE) {
701       XPUSHs(sv_2mortal(newSVpvs("s")));
702       XPUSHs(&PL_sv_yes);
703     }
704
705     /* /x */
706     if (self->rx->intflags & PMf_EXTENDED) {
707       XPUSHs(sv_2mortal(newSVpvs("x")));
708       XPUSHs(&PL_sv_yes);
709     }
710
711     /* /p */
712     if (self->rx->intflags & RXf_PMf_KEEPCOPY) {
713       XPUSHs(sv_2mortal(newSVpvs("p")));
714       XPUSHs(&PL_sv_yes);
715     }
716
717 void
718 stash(re::engine::Plugin self, ...)
719 PPCODE:
720     if (items > 1) {
721         self->stash = ST(1);
722         SvREFCNT_inc(self->stash);
723         XSRETURN_EMPTY;
724     } else {
725         XPUSHs(self->stash);
726     }
727
728 void
729 minlen(re::engine::Plugin self, ...)
730 PPCODE:
731     if (items > 1) {
732         self->rx->minlen = (I32)SvIV(ST(1));
733         XSRETURN_EMPTY;
734     } else {
735         if (self->rx->minlen) {
736             XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
737         } else {
738             XPUSHs(sv_2mortal(&PL_sv_undef));
739         }
740     }
741
742 void
743 gofs(re::engine::Plugin self, ...)
744 PPCODE:
745     if (items > 1) {
746         self->rx->gofs = (U32)SvIV(ST(1));
747         XSRETURN_EMPTY;
748     } else {
749         if (self->rx->gofs) {
750             XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
751         } else {
752             XPUSHs(sv_2mortal(&PL_sv_undef));
753         }
754     }
755
756 void
757 nparens(re::engine::Plugin self, ...)
758 PPCODE:
759     if (items > 1) {
760         self->rx->nparens = (U32)SvIV(ST(1));
761         XSRETURN_EMPTY;
762     } else {
763         if (self->rx->nparens) {
764             XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
765         } else {
766             XPUSHs(sv_2mortal(&PL_sv_undef));
767         }
768     }
769
770 void
771 _exec(re::engine::Plugin self, ...)
772 PPCODE:
773     if (items > 1) {
774         SvREFCNT_dec(self->cb_exec);
775         self->cb_exec = ST(1);
776         SvREFCNT_inc(self->cb_exec);
777     }
778
779 void
780 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
781 PPCODE:
782     if (items > 1) {
783         SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
784         self->cb_num_capture_buff_FETCH = ST(1);
785         SvREFCNT_inc(self->cb_num_capture_buff_FETCH);
786     }
787
788 void
789 _num_capture_buff_STORE(re::engine::Plugin self, ...)
790 PPCODE:
791     if (items > 1) {
792         SvREFCNT_dec(self->cb_num_capture_buff_STORE);
793         self->cb_num_capture_buff_STORE = ST(1);
794         SvREFCNT_inc(self->cb_num_capture_buff_STORE);
795     }
796
797 void
798 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
799 PPCODE:
800     if (items > 1) {
801         SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
802         self->cb_num_capture_buff_LENGTH = ST(1);
803         SvREFCNT_inc(self->cb_num_capture_buff_LENGTH);
804     }
805
806 SV *
807 _tag(SV *comp, SV *exec)
808 CODE:
809     RETVAL = rep_tag(comp, exec);
810 OUTPUT:
811     RETVAL
812
813 void
814 ENGINE()
815 PPCODE:
816     XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));