]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
The global context isn't needed before we check the hint
[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   dMY_CXT;
184   U32 hash = 0;
185   SV *pkg, *name, *pp_sv;
186
187   pkg  = av_pop(MY_CXT.next_pkg);
188   if (!SvOK(pkg))
189    return o;
190
191   name = av_pop(MY_CXT.next_name);
192   if (!SvOK(name)) {
193    SvREFCNT_dec(pkg);
194    return o;
195   }
196
197   {
198    HV *stash = gv_stashsv(pkg, 0);
199
200    if (stash) {
201     HE *he = hv_fetch_ent(stash, name, 0, 0);
202
203     if (he) {
204      CV *cv;
205      SV *gv = HeVAL(he);
206      hash   = HeHASH(he);
207
208      if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder){
209       SvREFCNT_dec(cv);
210       GvCV(gv) = NULL;
211       if (!GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIO(gv) && !GvFORM(gv))
212        (void) hv_delete_ent(stash, name, G_DISCARD, hash);
213      }
214     }
215    }
216   }
217
218   {
219    HE *he = hv_fetch_ent(MY_CXT.map, name, 0, hash);
220    if (!he)
221     goto skip;
222
223    pp_sv = HeVAL(he);
224    if (!SvOK(pp_sv))
225     goto skip;
226   }
227
228   if (o->op_type != OP_ENTERSUB)
229    goto skip;
230   if (o->op_private & OPpENTERSUB_AMPER) /* hopefully \&foo */
231    goto skip;
232
233   {
234    OP *ex_list  = cUNOPo->op_first;
235    OP *rv2cv, *gvop;
236    OP *last_arg = NULL;
237
238    /* pushmark when a method call */
239    if (!ex_list || ex_list->op_type != OP_NULL)
240     goto skip;
241
242    rv2cv = cUNOPx(ex_list)->op_first;
243    if (!rv2cv)
244     goto skip;
245
246    while (1) {
247     OP *next = rv2cv->op_sibling;
248     if (!next)
249      break;
250     last_arg = rv2cv;
251     rv2cv    = next;
252    }
253
254    if (!(rv2cv->op_flags & OPf_KIDS))
255     goto skip;
256
257    gvop = cUNOPx(rv2cv)->op_first;
258    if (!gvop || gvop->op_type != OP_GV)
259     goto skip;
260
261    {
262     GV         *gv   = cGVOPx_gv(gvop);
263     STRLEN      len;
264     const char *s    = SvPV_const(name, len);
265
266     if (GvNAMELEN(gv) == len && strnEQ(GvNAME(gv), s, len)) {
267      o->op_type   = OP_CUSTOM;
268      o->op_ppaddr = INT2PTR(Perl_ppaddr_t, SvIVX(pp_sv));
269
270      if (last_arg)
271       last_arg->op_sibling = NULL;
272      op_free(rv2cv);
273
274      {
275       MAGIC *mg = mg_find(pp_sv, PERL_MAGIC_ext);
276       if (mg) {
277        sub_op_check_t check = INT2PTR(sub_op_check_t, SvIVX(mg->mg_obj));
278        o = CALL_FPTR(check)(aTHX_ o, mg->mg_ptr);
279       }
280      }
281
282      sub_op_linklist(o);
283     }
284    }
285   }
286
287 skip:
288   SvREFCNT_dec(pkg);
289   SvREFCNT_dec(name);
290  }
291
292  return o;
293 }
294
295 /* --- XS ------------------------------------------------------------------ */
296
297 MODULE = Sub::Op      PACKAGE = Sub::Op
298
299 PROTOTYPES: ENABLE
300
301 BOOT:
302 {
303  MY_CXT_INIT;
304  MY_CXT.map         = newHV();
305  MY_CXT.next_pkg    = newAV();
306  MY_CXT.next_name   = newAV();
307  MY_CXT.placeholder = NULL;
308 #if SO_THREADSAFE
309  MY_CXT.owner       = aTHX;
310 #endif /* SO_THREADSAFE */
311
312  sub_op_old_ck_entersub = PL_check[OP_ENTERSUB];
313  PL_check[OP_ENTERSUB]  = sub_op_ck_entersub;
314 }
315
316 #if SO_THREADSAFE
317
318 void
319 CLONE(...)
320 PROTOTYPE: DISABLE
321 PREINIT:
322  HV  *map;
323  CV  *placeholder;
324  tTHX owner;
325 CODE:
326  {
327   dMY_CXT;
328   owner       = MY_CXT.owner;
329   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
330   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
331  }
332  {
333   MY_CXT_CLONE;
334   MY_CXT.map         = map;
335   MY_CXT.next_pkg    = newAV();
336   MY_CXT.next_name   = newAV();
337   MY_CXT.placeholder = placeholder;
338   MY_CXT.owner = aTHX;
339  }
340
341 #endif /* SO_THREADSAFE */
342
343 void
344 _placeholder(SV *sv)
345 PROTOTYPE: $
346 PPCODE:
347  if (SvROK(sv)) {
348   sv = SvRV(sv);
349   if (SvTYPE(sv) >= SVt_PVCV) {
350    dMY_CXT;
351    SvREFCNT_dec(MY_CXT.placeholder);
352    MY_CXT.placeholder = (CV *) SvREFCNT_inc(sv);
353   }
354  }
355  XSRETURN(0);
356
357 void
358 _incoming(SV *name, SV *pkg)
359 PROTOTYPE: $$
360 PPCODE:
361  dMY_CXT;
362  av_push(MY_CXT.next_pkg,  SvREFCNT_inc(pkg));
363  av_push(MY_CXT.next_name, SvREFCNT_inc(name));
364  XSRETURN(0);
365
366 void
367 _custom_name(SV *op)
368 PROTOTYPE: $
369 PREINIT:
370  OP *o;
371  SV *key;
372  HE *he;
373 PPCODE:
374  if (!SvROK(op))
375   XSRETURN_UNDEF;
376  o = INT2PTR(OP *, SvIV(SvRV(op)));
377  if (!o || o->op_type != OP_CUSTOM)
378   XSRETURN_UNDEF;
379  key = newSViv(PTR2IV(o->op_ppaddr));
380  he  = hv_fetch_ent(PL_custom_op_names, key, 0, 0);
381  SvREFCNT_dec(key);
382  if (!he)
383   XSRETURN_UNDEF;
384  ST(0) = sv_mortalcopy(HeVAL(he));
385  XSRETURN(1);