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