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