]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
Update VPIT::TestHelpers to 15e8aee3
[perl/modules/Sub-Op.git] / Op.xs
1 /* This file is part of the Sub::Op Perl module.
2  * See http://search.cpan.org/dist/Sub-Op/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "Sub::Op"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #define SO_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
15
16 #ifndef GvCV_set
17 # define GvCV_set(G, C) (GvCV(G) = (C))
18 #endif
19
20 /* ... Thread safety and multiplicity ...................................... */
21
22 #ifndef SO_MULTIPLICITY
23 # if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
24 #  define SO_MULTIPLICITY 1
25 # else
26 #  define SO_MULTIPLICITY 0
27 # endif
28 #endif
29 #if SO_MULTIPLICITY && !defined(tTHX)
30 # define tTHX PerlInterpreter*
31 #endif
32
33 #if SO_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))
34 # define SO_THREADSAFE 1
35 # ifndef MY_CXT_CLONE
36 #  define MY_CXT_CLONE \
37     dMY_CXT_SV;                                                      \
38     my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1)); \
39     Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
40     sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
41 # endif
42 #else
43 # define SO_THREADSAFE 0
44 # undef  dMY_CXT
45 # define dMY_CXT      dNOOP
46 # undef  MY_CXT
47 # define MY_CXT       indirect_globaldata
48 # undef  START_MY_CXT
49 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
50 # undef  MY_CXT_INIT
51 # define MY_CXT_INIT  NOOP
52 # undef  MY_CXT_CLONE
53 # define MY_CXT_CLONE NOOP
54 #endif
55
56 #if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK)
57 # define SO_CHECK_MUTEX_LOCK   OP_CHECK_MUTEX_LOCK
58 # define SO_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK
59 #else
60 # define SO_CHECK_MUTEX_LOCK   OP_REFCNT_LOCK
61 # define SO_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK
62 #endif
63
64 typedef OP *(*so_ck_t)(pTHX_ OP *);
65
66 #ifdef wrap_op_checker
67
68 # define so_ck_replace(T, NC, OCP) wrap_op_checker((T), (NC), (OCP))
69
70 #else
71
72 STATIC void so_ck_replace(pTHX_ OPCODE type, so_ck_t new_ck, so_ck_t *old_ck_p){
73 #define so_ck_replace(T, NC, OCP) so_ck_replace(aTHX_ (T), (NC), (OCP))
74  SO_CHECK_MUTEX_LOCK;
75  if (!*old_ck_p) {
76   *old_ck_p      = PL_check[type];
77   PL_check[type] = new_ck;
78  }
79  SO_CHECK_MUTEX_UNLOCK;
80 }
81
82 #endif
83
84 STATIC void so_ck_restore(pTHX_ OPCODE type, so_ck_t *old_ck_p) {
85 #define so_ck_restore(T, OCP) so_ck_restore(aTHX_ (T), (OCP))
86  SO_CHECK_MUTEX_LOCK;
87  if (*old_ck_p) {
88   PL_check[type] = *old_ck_p;
89   *old_ck_p      = 0;
90  }
91  SO_CHECK_MUTEX_UNLOCK;
92 }
93
94 /* --- Global data --------------------------------------------------------- */
95
96 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
97
98 typedef struct {
99  HV  *map;
100  CV  *placeholder;
101 #if SO_THREADSAFE
102  tTHX owner;
103 #endif /* SO_THREADSAFE */
104 } my_cxt_t;
105
106 START_MY_CXT
107
108 #if SO_THREADSAFE
109
110 STATIC SV *so_clone(pTHX_ SV *sv, tTHX owner) {
111 #define so_clone(S, O) so_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 #endif /* SO_THREADSAFE */
134
135 /* --- Public API ---------------------------------------------------------- */
136
137 #include "sub_op.h"
138
139 void sub_op_init(sub_op_config_t *c) {
140  c->name     = NULL;
141  c->namelen  = 0;
142  c->proto    = NULL;
143  c->protolen = 0;
144  c->call     = 0;
145  c->ref      = 0;
146  c->ud       = NULL;
147
148  return;
149 }
150
151 void sub_op_register(pTHX_ const sub_op_config_t *c, U32 flags) {
152  dMY_CXT;
153
154  if (!(flags & SUB_OP_REGISTER_STEAL))
155   c = sub_op_dup(aTHX_ c);
156
157  (void) hv_store(MY_CXT.map, c->name, c->namelen, newSViv(PTR2IV(c)), 0);
158 }
159
160 STATIC const char *so_strndup(pTHX_ const char *s, STRLEN len) {
161 #define so_strndup(S, L) so_strndup(aTHX_ (S), (L))
162  const char *d;
163
164  if (!s)
165   return NULL;
166
167  d = PerlMemShared_malloc(len + 1);
168  Copy(s, d, len, char);
169  ((char *) d)[len] = '\0';
170
171  return d;
172 }
173
174 sub_op_config_t *sub_op_dup(pTHX_ const sub_op_config_t *orig) {
175  sub_op_config_t *dupe = PerlMemShared_malloc(sizeof *dupe);
176
177  dupe->name    = so_strndup(orig->name, orig->namelen);
178  dupe->namelen = orig->namelen;
179
180  dupe->proto    = so_strndup(orig->proto, orig->protolen);
181  dupe->protolen = orig->protolen;
182
183  dupe->call = orig->call;
184  dupe->ref  = orig->ref;
185  dupe->ud   = orig->ud;
186
187  return dupe;
188 }
189
190 void sub_op_free(pTHX_ sub_op_config_t *c) {
191  PerlMemShared_free((char *) c->name);
192  PerlMemShared_free(c);
193
194  return;
195 }
196
197 OP *sub_op_study(const OP *o, OP **last_arg_p, OP **rv2cv_p) {
198  OP *ex_list, *last_arg, *rv2cv, *gvop;
199
200  ex_list = cUNOPo->op_first;
201  /* pushmark when a method call */
202  if (!ex_list || ex_list->op_type != OP_NULL)
203   goto skip;
204
205  rv2cv = cUNOPx(ex_list)->op_first;
206  if (!rv2cv)
207   goto skip;
208
209  while (1) {
210   OP *next = rv2cv->op_sibling;
211   if (!next)
212    break;
213   last_arg = rv2cv;
214   rv2cv    = next;
215  }
216
217  if (!(rv2cv->op_flags & OPf_KIDS))
218   goto skip;
219
220  gvop = cUNOPx(rv2cv)->op_first;
221
222  if (gvop && gvop->op_type == OP_GV)
223   goto done;
224
225 skip:
226  last_arg = NULL;
227  rv2cv    = NULL;
228  gvop     = NULL;
229
230 done:
231  if (last_arg_p)
232   *last_arg_p = last_arg;
233  if (rv2cv_p)
234   *rv2cv_p    = rv2cv;
235
236  return gvop;
237 }
238
239 /* --- Private helpers ----------------------------------------------------- */
240
241 STATIC IV so_hint(pTHX) {
242 #define so_hint() so_hint(aTHX)
243  SV *hint;
244
245 #if SO_HAS_PERL(5, 9, 5)
246  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
247                                        NULL,
248                                        __PACKAGE__, __PACKAGE_LEN__,
249                                        0,
250                                        0);
251 #else
252  {
253   SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
254   if (!val)
255    return 0;
256   hint = *val;
257  }
258 #endif
259
260  return (SvOK(hint) && SvIOK(hint)) ? SvIVX(hint) : 0;
261 }
262
263 STATIC OP *so_find_gvop(const OP *o) {
264  OP *ex_list, *last_arg, *rv2cv, *gvop;
265
266  ex_list = cUNOPo->op_first;
267  /* pushmark when a method call */
268  if (!ex_list || ex_list->op_type != OP_NULL)
269   goto skip;
270
271  rv2cv = cUNOPx(ex_list)->op_first;
272  if (!rv2cv)
273   goto skip;
274
275  while (1) {
276   OP *next = rv2cv->op_sibling;
277   if (!next)
278    break;
279   rv2cv = next;
280  }
281
282  if (!(rv2cv->op_flags & OPf_KIDS))
283   goto skip;
284
285  gvop = cUNOPx(rv2cv)->op_first;
286  if (gvop && gvop->op_type == OP_GV)
287   return gvop;
288
289 skip:
290  return NULL;
291 }
292
293 STATIC OP *(*so_old_ck_entersub)(pTHX_ OP *) = 0;
294
295 STATIC OP *so_ck_entersub(pTHX_ OP *o) {
296  o = CALL_FPTR(so_old_ck_entersub)(aTHX_ o);
297
298  if (so_hint()) {
299   OP *gvop;
300   GV *gv;
301
302   if (o->op_type != OP_ENTERSUB)
303    goto skip;
304   if (o->op_private & OPpENTERSUB_AMPER) /* hopefully \&foo */
305    goto skip;
306
307   gvop = so_find_gvop(o);
308   if (!gvop)
309    goto skip;
310
311   gv = cGVOPx_gv(gvop);
312
313   {
314    SV **svp;
315    CV  *cv = NULL;
316    const char *name = GvNAME(gv);
317    I32         len  = GvNAMELEN(gv);
318    const sub_op_config_t *c;
319    dMY_CXT;
320
321    svp = hv_fetch(MY_CXT.map, name, len, 0);
322    if (!svp)
323     goto skip;
324
325    c = INT2PTR(const sub_op_config_t *, SvIVX(*svp));
326
327    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
328     SvREFCNT_dec(cv);
329     GvCV_set(gv, NULL);
330    }
331
332    if (c->call)
333     o = CALL_FPTR(c->call)(aTHX_ o, c->ud);
334   }
335  }
336
337 skip:
338  return o;
339 }
340
341 STATIC OP *(*so_old_ck_refgen)(pTHX_ OP *) = 0;
342
343 STATIC OP *so_ck_refgen(pTHX_ OP *o) {
344  o = CALL_FPTR(so_old_ck_refgen)(aTHX_ o);
345
346  if (so_hint()) {
347   OP *kid    = o;
348   OP *prev   = NULL;
349   OP *parent = NULL;
350
351   while (kid->op_flags & OPf_KIDS) {
352    parent = kid;
353    kid    = cUNOPx(kid)->op_first;
354   }
355
356   if (!parent)
357    goto skip;
358
359   for (kid; kid; prev = kid, kid = kid->op_sibling) {
360    OP *gvop;
361    GV *gv;
362    const sub_op_config_t *c;
363
364    if (kid->op_type != OP_RV2CV)
365     continue;
366
367    gvop = so_find_gvop(kid);
368    if (!gvop)
369     continue;
370
371    gv = cGVOPx_gv(gvop);
372
373    {
374     SV **svp;
375     const char *name = GvNAME(gv);
376     I32         len  = GvNAMELEN(gv);
377     dMY_CXT;
378
379     svp = hv_fetch(MY_CXT.map, name, len, 0);
380     if (!svp)
381      continue;
382
383     c = INT2PTR(const sub_op_config_t *, SvIVX(*svp));
384    }
385
386    if (c->ref) {
387     OP *new_kid = CALL_FPTR(c->ref)(aTHX_ kid, c->ud);
388
389     if (new_kid != kid) {
390      new_kid->op_sibling = kid->op_sibling;
391      new_kid->op_next = new_kid;
392      if (prev)
393       prev->op_sibling = new_kid;
394      else
395       cUNOPx(parent)->op_first = new_kid;
396      op_null(kid);
397      kid = new_kid;
398     }
399    }
400   }
401  }
402
403 skip:
404  return o;
405 }
406
407 STATIC OP *(*so_old_ck_gelem)(pTHX_ OP *) = 0;
408
409 STATIC OP *so_ck_gelem(pTHX_ OP *o) {
410  o = CALL_FPTR(so_old_ck_entersub)(aTHX_ o);
411
412  if (so_hint()) {
413   OP *rv2gv, *gvop;
414   GV *gv;
415
416   rv2gv = cUNOPo->op_first;
417   if (!rv2gv)
418    goto skip;
419
420   gvop = cUNOPx(rv2gv)->op_first;
421   if (!gvop || gvop->op_type != OP_GV)
422    goto skip;
423
424   gv = cGVOPx_gv(gvop);
425   if (!gv)
426    goto skip;
427
428   {
429    CV *cv;
430    dMY_CXT;
431
432    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
433     SvREFCNT_dec(cv);
434     GvCV_set(gv, NULL);
435    }
436   }
437  }
438
439 skip:
440  return o;
441 }
442
443 /* --- XS ------------------------------------------------------------------ */
444
445 MODULE = Sub::Op      PACKAGE = Sub::Op
446
447 PROTOTYPES: ENABLE
448
449 BOOT:
450 {
451  MY_CXT_INIT;
452  MY_CXT.map         = newHV();
453  MY_CXT.placeholder = NULL;
454 #if SO_THREADSAFE
455  MY_CXT.owner       = aTHX;
456 #endif /* SO_THREADSAFE */
457
458  so_ck_replace(OP_ENTERSUB, so_ck_entersub, &so_old_ck_entersub);
459  so_ck_replace(OP_REFGEN,   so_ck_refgen,   &so_old_ck_refgen);
460  so_ck_replace(OP_GELEM,    so_ck_gelem,    &so_old_ck_gelem);
461 }
462
463 #if SO_THREADSAFE
464
465 void
466 CLONE(...)
467 PROTOTYPE: DISABLE
468 PREINIT:
469  HV  *map;
470  CV  *placeholder;
471  tTHX owner;
472 PPCODE:
473  {
474   dMY_CXT;
475   owner       = MY_CXT.owner;
476   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
477   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
478  }
479  {
480   MY_CXT_CLONE;
481   MY_CXT.map         = map;
482   MY_CXT.placeholder = placeholder;
483   MY_CXT.owner       = aTHX;
484  }
485  XSRETURN(0);
486
487 #endif /* SO_THREADSAFE */
488
489 void
490 _placeholder(SV *sv)
491 PROTOTYPE: $
492 PPCODE:
493  if (SvROK(sv)) {
494   sv = SvRV(sv);
495   if (SvTYPE(sv) >= SVt_PVCV) {
496    dMY_CXT;
497    SvREFCNT_dec(MY_CXT.placeholder);
498    MY_CXT.placeholder = (CV *) SvREFCNT_inc(sv);
499   }
500  }
501  XSRETURN(0);
502
503 void
504 _constant_sub(SV *sv)
505 PROTOTYPE: $
506 PPCODE:
507  if (!SvROK(sv))
508   XSRETURN_UNDEF;
509  sv = SvRV(sv);
510  if (SvTYPE(sv) < SVt_PVCV)
511   XSRETURN_UNDEF;
512  ST(0) = sv_2mortal(newSVuv(CvCONST(sv)));
513  XSRETURN(1);
514
515 SV *
516 _get_prototype(SV *name)
517 PROTOTYPE: $
518 PREINIT:
519  HE *he;
520  const sub_op_config_t *c;
521 PPCODE:
522  dMY_CXT;
523  he = hv_fetch_ent(MY_CXT.map, name, 0, 0);
524  if (!he)
525   XSRETURN_UNDEF;
526  c = INT2PTR(const sub_op_config_t *, SvIVX(HeVAL(he)));
527  if (!c->proto)
528   XSRETURN_UNDEF;
529  ST(0) = sv_2mortal(newSVpvn(c->proto, c->protolen));
530  XSRETURN(1);