]> git.vpit.fr Git - perl/modules/re-engine-Plugin.git/blob - Plugin.xs
Stop leaking the stash
[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  SvREFCNT_inc_simple_void_NN(obj);
213  Newxz(re, 1, struct replug);
214  sv_setref_pv(obj, XSH_PACKAGE, (void *) re);
215
216  newREGEXP(RX);
217  rx = rxREGEXP(RX);
218
219  re->rx       = rx;               /* Make the rx accessible from self->rx */
220  rx->intflags = flags;            /* Flags for internal use */
221  rx->extflags = flags;            /* Flags for perl to use */
222  rx->engine   = RE_ENGINE_PLUGIN; /* Compile to use this engine */
223
224 #if !XSH_HAS_PERL(5, 11, 0)
225  rx->refcnt   = 1;                /* Refcount so we won't be destroyed */
226
227  /* Precompiled pattern for pp_regcomp to use */
228  rx->prelen   = plen;
229  rx->precomp  = savepvn(pbuf, rx->prelen);
230
231  /* Set up qr// stringification to be equivalent to the supplied
232   * pattern, this should be done via overload eventually */
233  rx->wraplen  = rx->prelen;
234  Newx(rx->wrapped, rx->wraplen, char);
235  Copy(rx->precomp, rx->wrapped, rx->wraplen, char);
236 #endif
237
238  /* Store our private object */
239  rx->pprivate = obj;
240
241  /* Store the pattern for ->pattern */
242  re->pattern  = (SV *) pattern;
243  SvREFCNT_inc_simple_void(re->pattern);
244
245  /* If there's an exec callback, store it into the private object so that it
246   * will be the one to be called, even if the engine changes in between */
247  if (h->exec) {
248   re->cb_exec = h->exec;
249   SvREFCNT_inc_simple_void_NN(h->exec);
250  }
251
252  re->cb_num_capture_buff_FETCH  = NULL;
253  re->cb_num_capture_buff_STORE  = NULL;
254  re->cb_num_capture_buff_LENGTH = NULL;
255
256  /* Call our callback function if one was defined, if not we've already set up
257   * all the stuff we're going to to need for subsequent exec and other calls */
258  if (h->comp) {
259   ENTER;
260   SAVETMPS;
261
262   PUSHMARK(SP);
263   XPUSHs(obj);
264   PUTBACK;
265
266   call_sv(h->comp, G_DISCARD);
267
268   FREETMPS;
269   LEAVE;
270  }
271
272  /* If any of the comp-time accessors were called we'll have to
273   * update the regexp struct with the new info */
274  Newxz(rx->offs, rx->nparens + 1, regexp_paren_pair);
275
276  return RX;
277 }
278
279 I32
280 Plugin_exec(pTHX_ REGEXP * const RX, char *stringarg, char *strend,
281             char *strbeg, REP_ENG_EXEC_MINEND_TYPE minend,
282             SV *sv, void *data, U32 flags)
283 {
284  struct regexp *rx;
285  re__engine__Plugin self;
286  I32 matched;
287
288  rx = rxREGEXP(RX);
289  SELF_FROM_PPRIVATE(self, rx->pprivate);
290
291  if (self->cb_exec) {
292   SV *ret;
293   dSP;
294
295   /* Store the current str for ->str */
296   SvREFCNT_dec(self->str);
297   self->str = sv;
298   SvREFCNT_inc_simple_void(self->str);
299
300   ENTER;
301   SAVETMPS;
302
303   PUSHMARK(SP);
304   XPUSHs(rx->pprivate);
305   XPUSHs(sv);
306   PUTBACK;
307
308   call_sv(self->cb_exec, G_SCALAR);
309
310   SPAGAIN;
311
312   ret = POPs;
313   if (SvTRUE(ret))
314    matched = 1;
315   else
316    matched = 0;
317
318   PUTBACK;
319   FREETMPS;
320   LEAVE;
321  } else {
322   matched = 0;
323  }
324
325  return matched;
326 }
327
328 char *
329 #if XSH_HAS_PERL(5, 19, 1)
330 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, const char * const strbeg,
331               char *strpos, char *strend, U32 flags, re_scream_pos_data *data)
332 #else
333 Plugin_intuit(pTHX_ REGEXP * const RX, SV *sv, char *strpos,
334               char *strend, U32 flags, re_scream_pos_data *data)
335 #endif
336 {
337  PERL_UNUSED_ARG(RX);
338  PERL_UNUSED_ARG(sv);
339 #if XSH_HAS_PERL(5, 19, 1)
340  PERL_UNUSED_ARG(strbeg);
341 #endif
342  PERL_UNUSED_ARG(strpos);
343  PERL_UNUSED_ARG(strend);
344  PERL_UNUSED_ARG(flags);
345  PERL_UNUSED_ARG(data);
346
347  return NULL;
348 }
349
350 SV *
351 Plugin_checkstr(pTHX_ REGEXP * const RX)
352 {
353  PERL_UNUSED_ARG(RX);
354
355  return NULL;
356 }
357
358 void
359 Plugin_free(pTHX_ REGEXP * const RX)
360 {
361  struct regexp *rx;
362  re__engine__Plugin self;
363
364  if (PL_dirty)
365   return;
366
367  rx = rxREGEXP(RX);
368  SELF_FROM_PPRIVATE(self, rx->pprivate);
369
370  SvREFCNT_dec(self->pattern);
371  SvREFCNT_dec(self->str);
372  SvREFCNT_dec(self->stash);
373
374  SvREFCNT_dec(self->cb_exec);
375
376  SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
377  SvREFCNT_dec(self->cb_num_capture_buff_STORE);
378  SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
379
380  self->rx = NULL;
381  Safefree(self);
382
383 /*
384  dSP;
385  SV *callback;
386
387  callback = self->cb_free;
388
389  if (callback) {
390   ENTER;
391   SAVETMPS;
392
393   PUSHMARK(SP);
394   XPUSHs(rx->pprivate);
395   PUTBACK;
396
397   call_sv(callback, G_DISCARD);
398
399   PUTBACK;
400   FREETMPS;
401   LEAVE;
402  }
403  return;
404 */
405 }
406
407 void *
408 Plugin_dupe(pTHX_ REGEXP * const RX, CLONE_PARAMS *param)
409 {
410  struct regexp *rx = rxREGEXP(RX);
411
412  Perl_croak(aTHX_ "dupe not supported yet");
413
414  return rx->pprivate;
415 }
416
417
418 void
419 Plugin_numbered_buff_FETCH(pTHX_ REGEXP * const RX, const I32 paren,
420                            SV * const sv)
421 {
422  struct regexp *rx;
423  re__engine__Plugin self;
424  SV *callback;
425
426  rx = rxREGEXP(RX);
427  SELF_FROM_PPRIVATE(self, rx->pprivate);
428
429  callback = self->cb_num_capture_buff_FETCH;
430
431  if (callback) {
432   I32 items;
433   dSP;
434
435   ENTER;
436   SAVETMPS;
437
438   PUSHMARK(SP);
439   XPUSHs(rx->pprivate);
440   XPUSHs(sv_2mortal(newSViv(paren)));
441   PUTBACK;
442
443   items = call_sv(callback, G_SCALAR);
444
445   if (items == 1) {
446    SV *ret;
447    SPAGAIN;
448    ret = POPs;
449    sv_setsv(sv, ret);
450   } else {
451    sv_setsv(sv, &PL_sv_undef);
452   }
453
454   PUTBACK;
455   FREETMPS;
456   LEAVE;
457  } else {
458   sv_setsv(sv, &PL_sv_undef);
459  }
460 }
461
462 void
463 Plugin_numbered_buff_STORE(pTHX_ REGEXP * const RX, const I32 paren,
464                            SV const * const value)
465 {
466  struct regexp *rx;
467  re__engine__Plugin self;
468  SV *callback;
469
470  rx = rxREGEXP(RX);
471  SELF_FROM_PPRIVATE(self, rx->pprivate);
472
473  callback = self->cb_num_capture_buff_STORE;
474
475  if (callback) {
476   dSP;
477
478   ENTER;
479   SAVETMPS;
480
481   PUSHMARK(SP);
482   XPUSHs(rx->pprivate);
483   XPUSHs(sv_2mortal(newSViv(paren)));
484   XPUSHs((SV *) value);
485   PUTBACK;
486
487   call_sv(callback, G_DISCARD);
488
489   PUTBACK;
490   FREETMPS;
491   LEAVE;
492  }
493 }
494
495 I32
496 Plugin_numbered_buff_LENGTH(pTHX_ REGEXP * const RX, const SV * const sv,
497                             const I32 paren)
498 {
499  struct regexp *rx;
500  re__engine__Plugin self;
501  SV *callback;
502
503  rx = rxREGEXP(RX);
504  SELF_FROM_PPRIVATE(self, rx->pprivate);
505
506  callback = self->cb_num_capture_buff_LENGTH;
507
508  if (callback) {
509   IV ret;
510   dSP;
511
512   ENTER;
513   SAVETMPS;
514
515   PUSHMARK(SP);
516   XPUSHs(rx->pprivate);
517   XPUSHs(sv_2mortal(newSViv(paren)));
518   PUTBACK;
519
520   call_sv(callback, G_SCALAR);
521
522   SPAGAIN;
523
524   ret = POPi;
525
526   PUTBACK;
527   FREETMPS;
528   LEAVE;
529
530   return (I32) ret;
531  } else {
532   /* TODO: call FETCH and get the length on that value */
533   return 0;
534  }
535 }
536
537 SV *
538 Plugin_named_buff(pTHX_ REGEXP * const RX, SV * const key, SV * const value,
539                   const U32 flags)
540 {
541  return NULL;
542 }
543
544 SV *
545 Plugin_named_buff_iter(pTHX_ REGEXP * const RX, const SV * const lastkey,
546                        const U32 flags)
547 {
548  return NULL;
549 }
550
551 SV *
552 Plugin_package(pTHX_ REGEXP * const RX)
553 {
554  PERL_UNUSED_ARG(RX);
555
556  return newSVpvs(XSH_PACKAGE);
557 }
558
559 static void xsh_user_global_setup(pTHX) {
560  HV *stash;
561
562  stash = gv_stashpvn(XSH_PACKAGE, XSH_PACKAGE_LEN, 1);
563  newCONSTSUB(stash, "REP_THREADSAFE", newSVuv(XSH_THREADSAFE));
564  newCONSTSUB(stash, "REP_FORKSAFE",   newSVuv(XSH_FORKSAFE));
565
566  return;
567 }
568
569 /* --- XS ------------------------------------------------------------------ */
570
571 MODULE = re::engine::Plugin       PACKAGE = re::engine::Plugin
572
573 PROTOTYPES: DISABLE
574
575 BOOT:
576 {
577  xsh_setup();
578 }
579
580 #if XSH_THREADSAFE
581
582 void
583 CLONE(...)
584 PPCODE:
585  xsh_clone();
586  XSRETURN(0);
587
588 #endif /* XSH_THREADSAFE */
589
590 void
591 pattern(re::engine::Plugin self, ...)
592 PPCODE:
593  XPUSHs(self->pattern);
594  XSRETURN(1);
595
596 void
597 str(re::engine::Plugin self, ...)
598 PPCODE:
599  XPUSHs(self->str);
600  XSRETURN(1);
601
602 void
603 mod(re::engine::Plugin self)
604 PREINIT:
605  U32 flags;
606  char mods[5 + 1];
607  int n = 0, i;
608 PPCODE:
609  flags = self->rx->intflags;
610  if (flags & PMf_FOLD)         /* /i */
611   mods[n++] = 'i';
612  if (flags & PMf_MULTILINE)    /* /m */
613   mods[n++] = 'm';
614  if (flags & PMf_SINGLELINE)   /* /s */
615   mods[n++] = 's';
616  if (flags & PMf_EXTENDED)     /* /x */
617   mods[n++] = 'x';
618  if (flags & RXf_PMf_KEEPCOPY) /* /p */
619   mods[n++] = 'p';
620  mods[n] = '\0';
621  EXTEND(SP, 2 * n);
622  for (i = 0; i < n; ++i) {
623   mPUSHp(mods + i, 1);
624   PUSHs(&PL_sv_yes);
625  }
626  XSRETURN(2 * n);
627
628 void
629 stash(re::engine::Plugin self, ...)
630 PPCODE:
631  if (items > 1) {
632   SvREFCNT_dec(self->stash);
633   self->stash = ST(1);
634   SvREFCNT_inc_simple_void(self->stash);
635   XSRETURN_EMPTY;
636  } else {
637   XPUSHs(self->stash);
638   XSRETURN(1);
639  }
640
641 void
642 minlen(re::engine::Plugin self, ...)
643 PPCODE:
644  if (items > 1) {
645   self->rx->minlen = (I32)SvIV(ST(1));
646   XSRETURN_EMPTY;
647  } else {
648   if (self->rx->minlen) {
649    XPUSHs(sv_2mortal(newSViv(self->rx->minlen)));
650   } else {
651    XPUSHs(sv_2mortal(&PL_sv_undef));
652   }
653   XSRETURN(1);
654  }
655
656 void
657 gofs(re::engine::Plugin self, ...)
658 PPCODE:
659  if (items > 1) {
660   self->rx->gofs = (U32)SvIV(ST(1));
661   XSRETURN_EMPTY;
662  } else {
663   if (self->rx->gofs) {
664    XPUSHs(sv_2mortal(newSVuv(self->rx->gofs)));
665   } else {
666    XPUSHs(sv_2mortal(&PL_sv_undef));
667   }
668   XSRETURN(1);
669  }
670
671 void
672 nparens(re::engine::Plugin self, ...)
673 PPCODE:
674  if (items > 1) {
675   self->rx->nparens = (U32)SvIV(ST(1));
676   XSRETURN_EMPTY;
677  } else {
678   if (self->rx->nparens) {
679    XPUSHs(sv_2mortal(newSVuv(self->rx->nparens)));
680   } else {
681    XPUSHs(sv_2mortal(&PL_sv_undef));
682   }
683   XSRETURN(1);
684  }
685
686 void
687 _exec(re::engine::Plugin self, ...)
688 PPCODE:
689  if (items > 1) {
690   SvREFCNT_dec(self->cb_exec);
691   self->cb_exec = ST(1);
692   SvREFCNT_inc_simple_void(self->cb_exec);
693  }
694  XSRETURN(0);
695
696 void
697 _num_capture_buff_FETCH(re::engine::Plugin self, ...)
698 PPCODE:
699  if (items > 1) {
700   SvREFCNT_dec(self->cb_num_capture_buff_FETCH);
701   self->cb_num_capture_buff_FETCH = ST(1);
702   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_FETCH);
703  }
704  XSRETURN(0);
705
706 void
707 _num_capture_buff_STORE(re::engine::Plugin self, ...)
708 PPCODE:
709  if (items > 1) {
710   SvREFCNT_dec(self->cb_num_capture_buff_STORE);
711   self->cb_num_capture_buff_STORE = ST(1);
712   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_STORE);
713  }
714  XSRETURN(0);
715
716 void
717 _num_capture_buff_LENGTH(re::engine::Plugin self, ...)
718 PPCODE:
719  if (items > 1) {
720   SvREFCNT_dec(self->cb_num_capture_buff_LENGTH);
721   self->cb_num_capture_buff_LENGTH = ST(1);
722   SvREFCNT_inc_simple_void(self->cb_num_capture_buff_LENGTH);
723  }
724  XSRETURN(0);
725
726 SV *
727 _tag(SV *comp, SV *exec)
728 PREINIT:
729  xsh_hints_user_t arg;
730 CODE:
731  arg.comp = comp;
732  arg.exec = exec;
733  RETVAL = xsh_hints_tag(&arg);
734 OUTPUT:
735  RETVAL
736
737 void
738 ENGINE()
739 PPCODE:
740  XPUSHs(sv_2mortal(newSViv(PTR2IV(&engine_plugin))));
741  XSRETURN(1);