]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
Add support for the check hook and test multiple keywords
[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  AV  *next_pkg;
59  AV  *next_name;
60  CV  *placeholder;
61 #if SO_THREADSAFE
62  tTHX owner;
63 #endif /* SO_THREADSAFE */
64 } my_cxt_t;
65
66 START_MY_CXT
67
68 #if SO_THREADSAFE
69
70 STATIC SV *so_clone(pTHX_ SV *sv, tTHX owner) {
71 #define so_clone(S, O) so_clone(aTHX_ (S), (O))
72  CLONE_PARAMS  param;
73  AV           *stashes = NULL;
74  SV           *dupsv;
75
76  if (SvTYPE(sv) == SVt_PVHV && HvNAME_get(sv))
77   stashes = newAV();
78
79  param.stashes    = stashes;
80  param.flags      = 0;
81  param.proto_perl = owner;
82
83  dupsv = sv_dup(sv, &param);
84
85  if (stashes) {
86   av_undef(stashes);
87   SvREFCNT_dec(stashes);
88  }
89
90  return SvREFCNT_inc(dupsv);
91 }
92
93 #endif /* SO_THREADSAFE */
94
95 /* --- Public API ---------------------------------------------------------- */
96
97 #include "sub_op.h"
98
99 void sub_op_register(pTHX_ const sub_op_keyword *k) {
100  SV *key = newSViv(PTR2IV(k->pp));
101
102  if (!PL_custom_op_names)
103   PL_custom_op_names = newHV();
104  (void) hv_store_ent(PL_custom_op_names, key, newSVpv(k->name, k->len), 0);
105
106  if (!PL_custom_op_descs)
107   PL_custom_op_descs = newHV();
108  (void) hv_store_ent(PL_custom_op_descs, key, newSVpv(k->name, k->len), 0);
109
110  if (k->check) {
111   SV *check = newSViv(PTR2IV(k->check));
112   sv_magicext(key, check, PERL_MAGIC_ext, NULL, k->ud, 0);
113   SvREFCNT_dec(check);
114  }
115
116  {
117   dMY_CXT;
118   (void) hv_store(MY_CXT.map, k->name, k->len, key, 0);
119  }
120 }
121
122 /* --- Private helpers ----------------------------------------------------- */
123
124 #define SO_LINKLIST(O) ((O)->op_next ? (O)->op_next : sub_op_linklist(O))
125
126 STATIC OP *sub_op_linklist(pTHX_ OP *o) {
127 #define sub_op_linklist(O) sub_op_linklist(aTHX_ (O))
128     OP *first;
129
130     if (o->op_next)
131         return o->op_next;
132
133     /* establish postfix order */
134     first = cUNOPo->op_first;
135     if (first) {
136         register OP *kid;
137         o->op_next = SO_LINKLIST(first);
138         kid = first;
139         for (;;) {
140             if (kid->op_sibling) {
141                 kid->op_next = SO_LINKLIST(kid->op_sibling);
142                 kid = kid->op_sibling;
143             } else {
144                 kid->op_next = o;
145                 break;
146             }
147         }
148     }
149     else
150         o->op_next = o;
151
152     return o->op_next;
153 }
154
155 STATIC IV sub_op_hint(pTHX) {
156 #define sub_op_hint() sub_op_hint(aTHX)
157  SV *hint;
158
159 #if SO_HAS_PERL(5, 9, 5)
160  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
161                                        NULL,
162                                        __PACKAGE__, __PACKAGE_LEN__,
163                                        0,
164                                        0);
165 #else
166  {
167   SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
168   if (!val)
169    return 0;
170   hint = *val;
171  }
172 #endif
173
174  return (SvOK(hint) && SvIOK(hint)) ? SvIVX(hint) : 0;
175 }
176
177 STATIC OP *(*sub_op_old_ck_entersub)(pTHX_ OP *) = 0;
178
179 STATIC OP *sub_op_ck_entersub(pTHX_ OP *o) {
180  dMY_CXT;
181
182  o = CALL_FPTR(sub_op_old_ck_entersub)(aTHX_ o);
183
184  if (sub_op_hint()) {
185   dMY_CXT;
186   U32 hash = 0;
187   SV *pkg, *name, *pp_sv;
188
189   pkg  = av_pop(MY_CXT.next_pkg);
190   if (!SvOK(pkg))
191    return o;
192
193   name = av_pop(MY_CXT.next_name);
194   if (!SvOK(name)) {
195    SvREFCNT_dec(pkg);
196    return o;
197   }
198
199   {
200    HV *stash = gv_stashsv(pkg, 0);
201
202    if (stash) {
203     HE *he = hv_fetch_ent(stash, name, 0, 0);
204
205     if (he) {
206      CV *cv;
207      SV *gv = HeVAL(he);
208      hash   = HeHASH(he);
209
210      if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder){
211       SvREFCNT_dec(cv);
212       GvCV(gv) = NULL;
213       if (!GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIO(gv) && !GvFORM(gv))
214        (void) hv_delete_ent(stash, name, G_DISCARD, hash);
215      }
216     }
217    }
218   }
219
220   {
221    HE *he = hv_fetch_ent(MY_CXT.map, name, 0, hash);
222    if (!he)
223     goto skip;
224
225    pp_sv = HeVAL(he);
226    if (!SvOK(pp_sv))
227     goto skip;
228   }
229
230   if (o->op_type != OP_ENTERSUB)
231    goto skip;
232   if (o->op_private & OPpENTERSUB_AMPER) /* hopefully \&foo */
233    goto skip;
234
235   {
236    OP *ex_list  = cUNOPo->op_first;
237    OP *rv2cv, *gvop;
238    OP *last_arg = NULL;
239
240    /* pushmark when a method call */
241    if (!ex_list || ex_list->op_type != OP_NULL)
242     goto skip;
243
244    rv2cv = cUNOPx(ex_list)->op_first;
245    if (!rv2cv)
246     goto skip;
247
248    while (1) {
249     OP *next = rv2cv->op_sibling;
250     if (!next)
251      break;
252     last_arg = rv2cv;
253     rv2cv    = next;
254    }
255
256    if (!(rv2cv->op_flags & OPf_KIDS))
257     goto skip;
258
259    gvop = cUNOPx(rv2cv)->op_first;
260    if (!gvop || gvop->op_type != OP_GV)
261     goto skip;
262
263    {
264     GV         *gv   = cGVOPx_gv(gvop);
265     STRLEN      len;
266     const char *s    = SvPV_const(name, len);
267
268     if (GvNAMELEN(gv) == len && strnEQ(GvNAME(gv), s, len)) {
269      o->op_type   = OP_CUSTOM;
270      o->op_ppaddr = INT2PTR(Perl_ppaddr_t, SvIVX(pp_sv));
271
272      if (last_arg)
273       last_arg->op_sibling = NULL;
274      op_free(rv2cv);
275
276      {
277       MAGIC *mg = mg_find(pp_sv, PERL_MAGIC_ext);
278       if (mg) {
279        sub_op_check_t check = INT2PTR(sub_op_check_t, SvIVX(mg->mg_obj));
280        o = CALL_FPTR(check)(aTHX_ o, mg->mg_ptr);
281       }
282      }
283
284      sub_op_linklist(o);
285     }
286    }
287   }
288
289 skip:
290   SvREFCNT_dec(pkg);
291   SvREFCNT_dec(name);
292  }
293
294  return o;
295 }
296
297 /* --- XS ------------------------------------------------------------------ */
298
299 MODULE = Sub::Op      PACKAGE = Sub::Op
300
301 PROTOTYPES: ENABLE
302
303 BOOT:
304 {
305  MY_CXT_INIT;
306  MY_CXT.map         = newHV();
307  MY_CXT.next_pkg    = newAV();
308  MY_CXT.next_name   = newAV();
309  MY_CXT.placeholder = NULL;
310 #if SO_THREADSAFE
311  MY_CXT.owner       = aTHX;
312 #endif /* SO_THREADSAFE */
313
314  sub_op_old_ck_entersub = PL_check[OP_ENTERSUB];
315  PL_check[OP_ENTERSUB]  = sub_op_ck_entersub;
316 }
317
318 #if SO_THREADSAFE
319
320 void
321 CLONE(...)
322 PROTOTYPE: DISABLE
323 PREINIT:
324  HV  *map;
325  CV  *placeholder;
326  tTHX owner;
327 CODE:
328  {
329   dMY_CXT;
330   owner       = MY_CXT.owner;
331   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
332   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
333  }
334  {
335   MY_CXT_CLONE;
336   MY_CXT.map         = map;
337   MY_CXT.next_pkg    = newAV();
338   MY_CXT.next_name   = newAV();
339   MY_CXT.placeholder = placeholder;
340   MY_CXT.owner = aTHX;
341  }
342
343 #endif /* SO_THREADSAFE */
344
345 void
346 _placeholder(SV *sv)
347 PROTOTYPE: $
348 PPCODE:
349  if (SvROK(sv)) {
350   sv = SvRV(sv);
351   if (SvTYPE(sv) >= SVt_PVCV) {
352    dMY_CXT;
353    SvREFCNT_dec(MY_CXT.placeholder);
354    MY_CXT.placeholder = (CV *) SvREFCNT_inc(sv);
355   }
356  }
357  XSRETURN(0);
358
359 void
360 _incoming(SV *name, SV *pkg)
361 PROTOTYPE: $$
362 PPCODE:
363  dMY_CXT;
364  av_push(MY_CXT.next_pkg,  SvREFCNT_inc(pkg));
365  av_push(MY_CXT.next_name, SvREFCNT_inc(name));
366  XSRETURN(0);
367
368 void
369 _custom_name(SV *op)
370 PROTOTYPE: $
371 PREINIT:
372  OP *o;
373  SV *key;
374  HE *he;
375 PPCODE:
376  if (!SvROK(op))
377   XSRETURN_UNDEF;
378  o = INT2PTR(OP *, SvIV(SvRV(op)));
379  if (!o || o->op_type != OP_CUSTOM)
380   XSRETURN_UNDEF;
381  key = newSViv(PTR2IV(o->op_ppaddr));
382  he  = hv_fetch_ent(PL_custom_op_names, key, 0, 0);
383  SvREFCNT_dec(key);
384  if (!he)
385   XSRETURN_UNDEF;
386  ST(0) = sv_mortalcopy(HeVAL(he));
387  XSRETURN(1);