]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
Stop passing the package and the name from the magical callback to the check function
[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  o = CALL_FPTR(sub_op_old_ck_entersub)(aTHX_ o);
181
182  if (sub_op_hint()) {
183   OP *ex_list, *rv2cv, *gvop, *last_arg = NULL;
184   GV *gv;
185
186   if (o->op_type != OP_ENTERSUB)
187    goto skip;
188   if (o->op_private & OPpENTERSUB_AMPER) /* hopefully \&foo */
189    goto skip;
190
191   ex_list = cUNOPo->op_first;
192   /* pushmark when a method call */
193   if (!ex_list || ex_list->op_type != OP_NULL)
194    goto skip;
195
196   rv2cv = cUNOPx(ex_list)->op_first;
197   if (!rv2cv)
198    goto skip;
199
200   while (1) {
201    OP *next = rv2cv->op_sibling;
202    if (!next)
203     break;
204    last_arg = rv2cv;
205    rv2cv    = next;
206   }
207
208   if (!(rv2cv->op_flags & OPf_KIDS))
209    goto skip;
210
211   gvop = cUNOPx(rv2cv)->op_first;
212   if (!gvop || gvop->op_type != OP_GV)
213    goto skip;
214
215   gv = cGVOPx_gv(gvop);
216
217   {
218    HV *stash = GvSTASH(gv);
219    SV *pp_sv, **svp;
220    CV *cv = NULL;
221    const char *name = GvNAME(gv);
222    I32         len  = GvNAMELEN(gv);
223    dMY_CXT;
224
225    svp = hv_fetch(MY_CXT.map, name, len, 0);
226    if (!svp)
227     goto skip;
228
229    pp_sv = *svp;
230    if (!pp_sv || !SvOK(pp_sv))
231     goto skip;
232
233    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
234     SvREFCNT_dec(cv);
235     GvCV(gv) = NULL;
236     if (!GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIO(gv) && !GvFORM(gv)) {
237      (void) hv_delete(stash, name, len, G_DISCARD);
238     }
239    }
240
241    o->op_type   = OP_CUSTOM;
242    o->op_ppaddr = INT2PTR(Perl_ppaddr_t, SvIVX(pp_sv));
243
244    if (last_arg)
245     last_arg->op_sibling = NULL;
246
247    op_free(rv2cv);
248
249    {
250     MAGIC *mg = mg_find(pp_sv, PERL_MAGIC_ext);
251     if (mg) {
252      sub_op_check_t check = INT2PTR(sub_op_check_t, SvIVX(mg->mg_obj));
253      o = CALL_FPTR(check)(aTHX_ o, mg->mg_ptr);
254     }
255    }
256
257    sub_op_linklist(o);
258   }
259  }
260
261 skip:
262  return o;
263 }
264
265 STATIC OP *(*sub_op_old_ck_gelem)(pTHX_ OP *) = 0;
266
267 STATIC OP *sub_op_ck_gelem(pTHX_ OP *o) {
268  o = CALL_FPTR(sub_op_old_ck_entersub)(aTHX_ o);
269
270  if (sub_op_hint()) {
271   OP *rv2gv, *gvop;
272   GV *gv;
273
274   rv2gv = cUNOPo->op_first;
275   if (!rv2gv)
276    goto skip;
277
278   gvop = cUNOPx(rv2gv)->op_first;
279   if (!gvop || gvop->op_type != OP_GV)
280    goto skip;
281
282   gv = cGVOPx_gv(gvop);
283   if (!gv)
284    goto skip;
285
286   {
287    CV *cv;
288    dMY_CXT;
289
290    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
291     SvREFCNT_dec(cv);
292     GvCV(gv) = NULL;
293     if (!GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIO(gv) && !GvFORM(gv)) {
294      (void)hv_delete(GvSTASH(gv), GvNAME_get(gv), GvNAMELEN_get(gv), G_DISCARD);
295     }
296    }
297   }
298  }
299
300 skip:
301  return o;
302 }
303
304 /* --- XS ------------------------------------------------------------------ */
305
306 MODULE = Sub::Op      PACKAGE = Sub::Op
307
308 PROTOTYPES: ENABLE
309
310 BOOT:
311 {
312  MY_CXT_INIT;
313  MY_CXT.map         = newHV();
314  MY_CXT.next_pkg    = newAV();
315  MY_CXT.next_name   = newAV();
316  MY_CXT.placeholder = NULL;
317 #if SO_THREADSAFE
318  MY_CXT.owner       = aTHX;
319 #endif /* SO_THREADSAFE */
320
321  sub_op_old_ck_entersub = PL_check[OP_ENTERSUB];
322  PL_check[OP_ENTERSUB]  = sub_op_ck_entersub;
323  sub_op_old_ck_gelem    = PL_check[OP_GELEM];
324  PL_check[OP_GELEM]     = sub_op_ck_gelem;
325 }
326
327 #if SO_THREADSAFE
328
329 void
330 CLONE(...)
331 PROTOTYPE: DISABLE
332 PREINIT:
333  HV  *map;
334  CV  *placeholder;
335  tTHX owner;
336 CODE:
337  {
338   dMY_CXT;
339   owner       = MY_CXT.owner;
340   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
341   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
342  }
343  {
344   MY_CXT_CLONE;
345   MY_CXT.map         = map;
346   MY_CXT.next_pkg    = newAV();
347   MY_CXT.next_name   = newAV();
348   MY_CXT.placeholder = placeholder;
349   MY_CXT.owner       = aTHX;
350  }
351
352 #endif /* SO_THREADSAFE */
353
354 void
355 _placeholder(SV *sv)
356 PROTOTYPE: $
357 PPCODE:
358  if (SvROK(sv)) {
359   sv = SvRV(sv);
360   if (SvTYPE(sv) >= SVt_PVCV) {
361    dMY_CXT;
362    SvREFCNT_dec(MY_CXT.placeholder);
363    MY_CXT.placeholder = (CV *) SvREFCNT_inc(sv);
364   }
365  }
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);