]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
Stop leaking the underlying re::engine::Plugin object
[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 /* --- Helpers ------------------------------------------------------------- */
10
11 #define XSH_PACKAGE "re::engine::Plugin"
12
13 #include "xsh/caps.h"
14 #include "xsh/util.h"
15
16 /* ... Lexical hints ....................................................... */
17
18 typedef struct {
19  SV *comp;
20  SV *exec;
21 } xsh_hints_user_t;
22
23 static SV *rep_validate_callback(SV *code) {
24  if (!SvROK(code))
25   return NULL;
26
27  code = SvRV(code);
28  if (SvTYPE(code) < SVt_PVCV)
29   return NULL;
30
31  return SvREFCNT_inc_simple_NN(code);
32 }
33
34 static void xsh_hints_user_init(pTHX_ xsh_hints_user_t *hv, xsh_hints_user_t *v) {
35  hv->comp = rep_validate_callback(v->comp);
36  hv->exec = rep_validate_callback(v->exec);
37
38  return;
39 }
40
41 #if XSH_THREADSAFE
42
43 static void xsh_hints_user_clone(pTHX_ xsh_hints_user_t *nv, xsh_hints_user_t *ov, CLONE_PARAMS *params) {
44  nv->comp = xsh_dup_inc(ov->comp, params);
45  nv->exec = xsh_dup_inc(ov->exec, params);
46
47  return;
48 }
49
50 #endif /* XSH_THREADSAFE */
51
52 static void xsh_hints_user_deinit(pTHX_ xsh_hints_user_t *hv) {
53  SvREFCNT_dec(hv->comp);
54  SvREFCNT_dec(hv->exec);
55
56  return;
57 }
58
59 #define rep_hint() xsh_hints_detag(xsh_hints_fetch())
60
61 #define XSH_HINTS_TYPE_USER         1
62 #define XSH_HINTS_ONLY_COMPILE_TIME 0
63
64 #include "xsh/hints.h"
65
66 /* ... Thread-local storage ................................................ */
67
68 #define XSH_THREADS_USER_CONTEXT            0
69 #define XSH_THREADS_USER_LOCAL_SETUP        0
70 #define XSH_THREADS_USER_LOCAL_TEARDOWN     0
71 #define XSH_THREADS_USER_GLOBAL_TEARDOWN    0
72 #define XSH_THREADS_COMPILE_TIME_PROTECTION 0
73
74 #include "xsh/threads.h"
75
76 /* --- Custom regexp engine ------------------------------------------------ */
77
78 /* re__engine__Plugin self; SELF_FROM_PPRIVATE(self,rx->pprivate) */
79 #define SELF_FROM_PPRIVATE(self, pprivate) \
80  if (sv_isobject(pprivate)) {              \
81   SV *ref = SvRV((SV *) pprivate);         \
82   IV  tmp = SvIV((SV *) ref);              \
83   self = INT2PTR(re__engine__Plugin, tmp); \
84  } else {                                  \
85   Perl_croak(aTHX_ "Not an object");       \
86  }
87
88 #if XSH_HAS_PERL(5, 19, 4)
89 # define REP_ENG_EXEC_MINEND_TYPE SSize_t
90 #else
91 # define REP_ENG_EXEC_MINEND_TYPE I32
92 #endif
93
94 START_EXTERN_C
95 EXTERN_C const regexp_engine engine_plugin;
96 #if XSH_HAS_PERL(5, 11, 0)
97 EXTERN_C REGEXP * Plugin_comp(pTHX_ SV * const, U32);
98 #else
99 EXTERN_C REGEXP * Plugin_comp(pTHX_ const SV * const, const U32);
100 #endif
101 EXTERN_C I32      Plugin_exec(pTHX_ REGEXP * const, char *, char *,
102                               char *, REP_ENG_EXEC_MINEND_TYPE, SV *, void *, U32);
103 #if XSH_HAS_PERL(5, 19, 1)
104 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, const char * const,
105                                 char *, char *, U32, re_scream_pos_data *);
106 #else
107 EXTERN_C char *   Plugin_intuit(pTHX_ REGEXP * const, SV *, char *,
108                                 char *, U32, re_scream_pos_data *);
109 #endif
110 EXTERN_C SV *     Plugin_checkstr(pTHX_ REGEXP * const);
111 EXTERN_C void     Plugin_free(pTHX_ REGEXP * const);
112 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
113 EXTERN_C void     Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const,
114                                              const I32, SV * const);
115 EXTERN_C void     Plugin_numbered_buff_STORE(pTHX_ REGEXP * const,
116                                              const I32, SV const * const);
117 EXTERN_C I32      Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const,
118                                               const SV * const, const I32);
119 EXTERN_C SV *     Plugin_named_buff(pTHX_ REGEXP * const, SV * const,
120                                     SV * const, const U32);
121 EXTERN_C SV *     Plugin_named_buff_iter(pTHX_ REGEXP * const, const SV * const,
122                                          const U32);
123 EXTERN_C SV *     Plugin_package(pTHX_ REGEXP * const);
124 #ifdef USE_ITHREADS
125 EXTERN_C void *   Plugin_dupe(pTHX_ REGEXP * const, CLONE_PARAMS *);
126 #endif
127
128 EXTERN_C const regexp_engine engine_plugin;
129 END_EXTERN_C
130
131 #define RE_ENGINE_PLUGIN (&engine_plugin)
132 const regexp_engine engine_plugin = {
133  Plugin_comp,
134  Plugin_exec,
135  Plugin_intuit,
136  Plugin_checkstr,
137  Plugin_free,
138  Plugin_numbered_buff_FETCH,
139  Plugin_numbered_buff_STORE,
140  Plugin_numbered_buff_LENGTH,
141  Plugin_named_buff,
142  Plugin_named_buff_iter,
143  Plugin_package
144 #if defined(USE_ITHREADS)
145  , Plugin_dupe
146 #endif
147 #if XSH_HAS_PERL(5, 17, 0)
148  , 0
149 #endif
150 };
151
152 typedef struct replug {
153  /* Pointer back to the containing regexp struct so that accessors
154   * can modify nparens, gofs, etc... */
155  struct regexp *rx;
156
157  /* A copy of the pattern given to comp, for ->pattern */
158  SV *pattern;
159
160  /* A copy of the string being matched against, for ->str */
161  SV *str;
162
163  /* The ->stash */
164  SV *stash;
165
166  /* Callbacks */
167  SV *cb_exec;
168  SV *cb_free;
169
170  /* ->num_captures */
171  SV *cb_num_capture_buff_FETCH;
172  SV *cb_num_capture_buff_STORE;
173  SV *cb_num_capture_buff_LENGTH;
174 } *re__engine__Plugin;
175
176 #if XSH_HAS_PERL(5, 11, 0)
177 # define rxREGEXP(RX)  (SvANY(RX))
178 # define newREGEXP(RX) ((RX) = ((REGEXP *) newSV_type(SVt_REGEXP)))
179 #else
180 # define rxREGEXP(RX)  (RX)
181 # define newREGEXP(RX) (Newxz((RX), 1, struct regexp))
182 #endif
183
184 REGEXP *
185 #if XSH_HAS_PERL(5, 11, 0)
186 Plugin_comp(pTHX_ SV * const pattern, U32 flags)
187 #else
188 Plugin_comp(pTHX_ const SV * const pattern, const U32 flags)
189 #endif
190 {
191  dSP;
192  struct regexp *rx;
193  REGEXP *RX;
194
195  re__engine__Plugin re;
196  const xsh_hints_user_t *h;
197
198  STRLEN plen;
199  char *pbuf;
200
201  SV *obj;
202
203  h = rep_hint();
204  if (!h) /* This looks like a pragma leak. Apply the default behaviour */
205   return re_compile(pattern, flags);
206
207  /* exp/xend version of the pattern & length */
208  pbuf = SvPV((SV *) pattern, plen);
209
210  /* Our blessed object */
211  obj = newSV(0);
212  Newxz(re, 1, struct replug);
213  sv_setref_pv(obj, XSH_PACKAGE, (void *) re);
214
215  newREGEXP(RX);
216  rx = rxREGEXP(RX);
217
218  re->rx       = rx;               /* Make the rx accessible from self->rx */
219  rx->intflags = flags;            /* Flags for internal use */
220  rx->extflags = flags;            /* Flags for perl to use */
221  rx->engine   = RE_ENGINE_PLUGIN; /* Compile to use this engine */
222
223 #if !XSH_HAS_PERL(5, 11, 0)
224  rx->refcnt   = 1;                /* Refcount so we won't be destroyed */
225
226  /* Precompiled pattern for pp_regcomp to use */
227  rx->prelen   = plen;
228  rx->precomp  = savepvn(pbuf, rx->prelen);
229
230  /* Set up qr// stringification to be equivalent to the supplied
231   * pattern, this should be done via overload eventually */
232  rx->wraplen  = rx->prelen;
233  Newx(rx->wrapped, rx->wraplen, char);
234  Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
235 #endif
236
237  /* Store our private object */
238  rx->pprivate = obj;
239
240  /* Store the pattern for ->pattern */
241  re->pattern  = (SV *) pattern;
242  SvREFCNT_inc_simple_void(re->pattern);
243
244  /* If there's an exec callback, store it into the private object so that it
245   * will be the one to be called, even if the engine changes in between */
246  if (h->exec) {
247   re->cb_exec = h->exec;
248   SvREFCNT_inc_simple_void_NN(h->exec);
249  }
250
251  re->cb_num_capture_buff_FETCH  = NULL;
252  re->cb_num_capture_buff_STORE  = NULL;
253  re->cb_num_capture_buff_LENGTH = NULL;
254
255  /* Call our callback function if one was defined, if not we've already set up
256   * all the stuff we're going to to need for subsequent exec and other calls */
257  if (h->comp) {
258   ENTER;
259   SAVETMPS;
260
261   PUSHMARK(SP);
262   XPUSHs(obj);
263   PUTBACK;
264
265   call_sv(h->comp, G_DISCARD);
266
267   FREETMPS;
268   LEAVE;
269  }
270
271  /* If any of the comp-time accessors were called we'll have to
272   * update the regexp struct with the new info */
273  Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair);
274
275  return RX;
276 }
277
278 I32
279 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
280             char *strbeg, REP_ENG_EXEC_MINEND_TYPE minend,
281             SV *sv, void *data, U32 flags)
282 {
283  struct regexp *rx;
284  re__engine__Plugin self;
285  I32 matched;
286
287  rx = rxREGEXP(RX);
288  SELF_FROM_PPRIVATE(self, rx->pprivate);
289
290  if (self->cb_exec) {
291   SV *ret;
292   dSP;
293
294   /* Store the current str for ->str */
295   SvREFCNT_dec(self->str);
296   self->str = sv;
297   SvREFCNT_inc_simple_void(self->str);
298
299   ENTER;
300   SAVETMPS;
301
302   PUSHMARK(SP);
303   XPUSHs(rx->pprivate);
304   XPUSHs(sv);
305   PUTBACK;
306
307   call_sv(self->cb_exec, G_SCALAR);
308
309   SPAGAIN;
310
311   ret = POPs;
312   if (SvTRUE(ret))
313    matched = 1;
314   else
315    matched = 0;
316
317   PUTBACK;
318   FREETMPS;
319   LEAVE;
320  } else {
321   matched = 0;
322  }
323
324  return matched;
325 }
326
327 char *
328 #if XSH_HAS_PERL(5, 19, 1)
329 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, const char * const strbeg,
330               char *strpos, char *strend, U32 flags, re_scream_pos_data *data)
331 #else
332 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
333               char *strend, U32 flags, re_scream_pos_data *data)
334 #endif
335 {
336  PERL_UNUSED_ARG(RX);
337  PERL_UNUSED_ARG(sv);
338 #if XSH_HAS_PERL(5, 19, 1)
339  PERL_UNUSED_ARG(strbeg);
340 #endif
341  PERL_UNUSED_ARG(strpos);
342  PERL_UNUSED_ARG(strend);
343  PERL_UNUSED_ARG(flags);
344  PERL_UNUSED_ARG(data);
345
346  return NULL;
347 }
348
349 SV *
350 Plugin_checkstr(pTHX_ REGEXP * const RX)
351 {
352  PERL_UNUSED_ARG(RX);
353
354  return NULL;
355 }
356
357 void
358 Plugin_free(pTHX_ REGEXP * const RX)
359 {
360  struct regexp *rx;
361  re__engine__Plugin self;
362
363  if (PL_dirty)
364   return;
365
366  rx = rxREGEXP(RX);
367  SELF_FROM_PPRIVATE(self, rx->pprivate);
368
369  SvREFCNT_dec(self->pattern);
370  SvREFCNT_dec(self->str);
371  SvREFCNT_dec(self->stash);
372
373  SvREFCNT_dec(self->cb_exec);
374
375  SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
376  SvREFCNT_dec(self->cb_num_capture_buff_STORE);
377  SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
378
379  self->rx = NULL;
380
381  Safefree(self);
382
383  SvREFCNT_dec(rx->pprivate);
384
385 /*
386  dSP;
387  SV *callback;
388
389  callback = self->cb_free;
390
391  if (callback) {
392   ENTER;
393   SAVETMPS;
394
395   PUSHMARK(SP);
396   XPUSHs(rx->pprivate);
397   PUTBACK;
398
399   call_sv(callback, G_DISCARD);
400
401   PUTBACK;
402   FREETMPS;
403   LEAVE;
404  }
405  return;
406 */
407 }
408
409 void *
410 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
411 {
412  struct regexp *rx = rxREGEXP(RX);
413
414  Perl_croak(aTHX_ "dupe not supported yet");
415
416  return rx->pprivate;
417 }
418
419
420 void
421 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
422                            SV * const sv)
423 {
424  struct regexp *rx;
425  re__engine__Plugin self;
426  SV *callback;
427
428  rx = rxREGEXP(RX);
429  SELF_FROM_PPRIVATE(self, rx->pprivate);
430
431  callback = self->cb_num_capture_buff_FETCH;
432
433  if (callback) {
434   I32 items;
435   dSP;
436
437   ENTER;
438   SAVETMPS;
439
440   PUSHMARK(SP);
441   XPUSHs(rx->pprivate);
442   XPUSHs(sv_2mortal(newSViv(paren)));
443   PUTBACK;
444
445   items = call_sv(callback, G_SCALAR);
446
447   if (items == 1) {
448    SV *ret;
449    SPAGAIN;
450    ret = POPs;
451    sv_setsv(sv, ret);
452   } else {
453    sv_setsv(sv, &PL_sv_undef);
454   }
455
456   PUTBACK;
457   FREETMPS;
458   LEAVE;
459  } else {
460   sv_setsv(sv, &PL_sv_undef);
461  }
462 }
463
464 void
465 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
466                            SV const * const value)
467 {
468  struct regexp *rx;
469  re__engine__Plugin self;
470  SV *callback;
471
472  rx = rxREGEXP(RX);
473  SELF_FROM_PPRIVATE(self, rx->pprivate);
474
475  callback = self->cb_num_capture_buff_STORE;
476
477  if (callback) {
478   dSP;
479
480   ENTER;
481   SAVETMPS;
482
483   PUSHMARK(SP);
484   XPUSHs(rx->pprivate);
485   XPUSHs(sv_2mortal(newSViv(paren)));
486   XPUSHs((SV *) value);
487   PUTBACK;
488
489   call_sv(callback, G_DISCARD);
490
491   PUTBACK;
492   FREETMPS;
493   LEAVE;
494  }
495 }
496
497 I32
498 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
499                             const I32 paren)
500 {
501  struct regexp *rx;
502  re__engine__Plugin self;
503  SV *callback;
504
505  rx = rxREGEXP(RX);
506  SELF_FROM_PPRIVATE(self, rx->pprivate);
507
508  callback = self->cb_num_capture_buff_LENGTH;
509
510  if (callback) {
511   IV ret;
512   dSP;
513
514   ENTER;
515   SAVETMPS;
516
517   PUSHMARK(SP);
518   XPUSHs(rx->pprivate);
519   XPUSHs(sv_2mortal(newSViv(paren)));
520   PUTBACK;
521
522   call_sv(callback, G_SCALAR);
523
524   SPAGAIN;
525
526   ret = POPi;
527
528   PUTBACK;
529   FREETMPS;
530   LEAVE;
531
532   return (I32) ret;
533  } else {
534   /* TODO: call FETCH and get the length on that value */
535   return 0;
536  }
537 }
538
539 SV *
540 Plugin_named_buff(pTHX_ REGEXP * const RX, SV * const key, SV * const value,
541                   const U32 flags)
542 {
543  return NULL;
544 }
545
546 SV *
547 Plugin_named_buff_iter(pTHX_ REGEXP * const RX, const SV * const lastkey,
548                        const U32 flags)
549 {
550  return NULL;
551 }
552
553 SV *
554 Plugin_package(pTHX_ REGEXP * const RX)
555 {
556  PERL_UNUSED_ARG(RX);
557
558  return newSVpvs(XSH_PACKAGE);
559 }
560
561 static void xsh_user_global_setup(pTHX) {
562  HV *stash;
563
564  stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1);
565  newCONSTSUB(stash, "REP_THREADSAFE", newSVuv(XSH_THREADSAFE));
566  newCONSTSUB(stash, "REP_FORKSAFE",   newSVuv(XSH_FORKSAFE));
567
568  return;
569 }
570
571 /* --- XS ------------------------------------------------------------------ */
572
573 MODULE = re::engine::Plugin       PACKAGE = re::engine::Plugin
574
575 PROTOTYPES: DISABLE
576
577 BOOT:
578 {
579  xsh_setup();
580 }
581
582 #if XSH_THREADSAFE
583
584 void
585 CLONE(...)
586 PPCODE:
587  xsh_clone();
588  XSRETURN(0);
589
590 #endif /* XSH_THREADSAFE */
591
592 void
593 pattern(re::engine::Plugin self, ...)
594 PPCODE:
595  XPUSHs(self->pattern);
596  XSRETURN(1);
597
598 void
599 str(re::engine::Plugin self, ...)
600 PPCODE:
601  XPUSHs(self->str);
602  XSRETURN(1);
603
604 void
605 mod(re::engine::Plugin self)
606 PREINIT:
607  U32 flags;
608  char mods[5 + 1];
609  int n = 0, i;
610 PPCODE:
611  flags = self->rx->intflags;
612  if (flags & PMf_FOLD)         /* /i */
613   mods[n++] = 'i';
614  if (flags & PMf_MULTILINE)    /* /m */
615   mods[n++] = 'm';
616  if (flags & PMf_SINGLELINE)   /* /s */
617   mods[n++] = 's';
618  if (flags & PMf_EXTENDED)     /* /x */
619   mods[n++] = 'x';
620  if (flags & RXf_PMf_KEEPCOPY) /* /p */
621   mods[n++] = 'p';
622  mods[n] = '\0';
623  EXTEND(SP, 2 * n);
624  for (i = 0; i < n; ++i) {
625   mPUSHp(mods + i, 1);
626   PUSHs(&PL_sv_yes);
627  }
628  XSRETURN(2 * n);
629
630 void
631 stash(re::engine::Plugin self, ...)
632 PPCODE:
633  if (items > 1) {
634   SvREFCNT_dec(self->stash);
635   self->stash = ST(1);
636   SvREFCNT_inc_simple_void(self->stash);
637   XSRETURN_EMPTY;
638  } else {
639   XPUSHs(self->stash);
640   XSRETURN(1);
641  }
642
643 void
644 minlen(re::engine::Plugin self, ...)
645 PPCODE:
646  if (items > 1) {
647   self->rx->minlen = (I32)SvIV(ST(1));
648   XSRETURN_EMPTY;
649  } else {
650   if (self->rx->minlen) {
651    XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
652   } else {
653    XPUSHs(sv_2mortal(&PL_sv_undef));
654   }
655   XSRETURN(1);
656  }
657
658 void
659 gofs(re::engine::Plugin self, ...)
660 PPCODE:
661  if (items > 1) {
662   self->rx->gofs = (U32)SvIV(ST(1));
663   XSRETURN_EMPTY;
664  } else {
665   if (self->rx->gofs) {
666    XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
667   } else {
668    XPUSHs(sv_2mortal(&PL_sv_undef));
669   }
670   XSRETURN(1);
671  }
672
673 void
674 nparens(re::engine::Plugin self, ...)
675 PPCODE:
676  if (items > 1) {
677   self->rx->nparens = (U32)SvIV(ST(1));
678   XSRETURN_EMPTY;
679  } else {
680   if (self->rx->nparens) {
681    XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
682   } else {
683    XPUSHs(sv_2mortal(&PL_sv_undef));
684   }
685   XSRETURN(1);
686  }
687
688 void
689 _exec(re::engine::Plugin self, ...)
690 PPCODE:
691  if (items > 1) {
692   SvREFCNT_dec(self->cb_exec);
693   self->cb_exec = ST(1);
694   SvREFCNT_inc_simple_void(self->cb_exec);
695  }
696  XSRETURN(0);
697
698 void
699 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
700 PPCODE:
701  if (items > 1) {
702   SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
703   self->cb_num_capture_buff_FETCH = ST(1);
704   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH);
705  }
706  XSRETURN(0);
707
708 void
709 _num_capture_buff_STORE(re::engine::Plugin self, ...)
710 PPCODE:
711  if (items > 1) {
712   SvREFCNT_dec(self->cb_num_capture_buff_STORE);
713   self->cb_num_capture_buff_STORE = ST(1);
714   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE);
715  }
716  XSRETURN(0);
717
718 void
719 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
720 PPCODE:
721  if (items > 1) {
722   SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
723   self->cb_num_capture_buff_LENGTH = ST(1);
724   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH);
725  }
726  XSRETURN(0);
727
728 SV *
729 _tag(SV *comp, SV *exec)
730 PREINIT:
731  xsh_hints_user_t arg;
732 CODE:
733  arg.comp = comp;
734  arg.exec = exec;
735  RETVAL = xsh_hints_tag(&arg);
736 OUTPUT:
737  RETVAL
738
739 void
740 ENGINE()
741 PPCODE:
742  XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));
743  XSRETURN(1);