]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
Rename sub_op_keyword to sub_op_config_t
[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 #define PTABLE_NAME        ptable
94 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
95
96 #include "ptable.h"
97
98 /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */
99 #define ptable_store(T, K, V) ptable_store(aPTBLMS_ (T), (K), (V))
100
101 STATIC ptable *so_op_name = NULL;
102
103 #ifdef USE_ITHREADS
104 STATIC perl_mutex so_op_name_mutex;
105 #endif
106
107 typedef struct {
108  STRLEN len;
109  char   buf;
110 } so_op_name_t;
111
112 /* --- Public API ---------------------------------------------------------- */
113
114 #include "sub_op.h"
115
116 void sub_op_register(pTHX_ const sub_op_config_t *c) {
117  SV *key = newSViv(PTR2IV(c->pp));
118
119  if (!PL_custom_op_names)
120   PL_custom_op_names = newHV();
121  (void) hv_store_ent(PL_custom_op_names, key, newSVpv(c->name, c->len), 0);
122
123  if (!PL_custom_op_descs)
124   PL_custom_op_descs = newHV();
125  (void) hv_store_ent(PL_custom_op_descs, key, newSVpv(c->name, c->len), 0);
126
127  if (c->check) {
128   SV *check = newSViv(PTR2IV(c->check));
129   sv_magicext(key, check, PERL_MAGIC_ext, NULL, c->ud, 0);
130   SvREFCNT_dec(check);
131  }
132
133  {
134   dMY_CXT;
135   (void) hv_store(MY_CXT.map, c->name, c->len, key, 0);
136  }
137 }
138
139 /* --- Private helpers ----------------------------------------------------- */
140
141 STATIC IV so_hint(pTHX) {
142 #define so_hint() so_hint(aTHX)
143  SV *hint;
144
145 #if SO_HAS_PERL(5, 9, 5)
146  hint = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
147                                        NULL,
148                                        __PACKAGE__, __PACKAGE_LEN__,
149                                        0,
150                                        0);
151 #else
152  {
153   SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, 0);
154   if (!val)
155    return 0;
156   hint = *val;
157  }
158 #endif
159
160  return (SvOK(hint) && SvIOK(hint)) ? SvIVX(hint) : 0;
161 }
162
163 STATIC OP *(*so_old_ck_entersub)(pTHX_ OP *) = 0;
164
165 STATIC OP *so_ck_entersub(pTHX_ OP *o) {
166  o = CALL_FPTR(so_old_ck_entersub)(aTHX_ o);
167
168  if (so_hint()) {
169   OP *ex_list, *rv2cv, *gvop, *last_arg = NULL;
170   GV *gv;
171
172   if (o->op_type != OP_ENTERSUB)
173    goto skip;
174   if (o->op_private & OPpENTERSUB_AMPER) /* hopefully \&foo */
175    goto skip;
176
177   ex_list = cUNOPo->op_first;
178   /* pushmark when a method call */
179   if (!ex_list || ex_list->op_type != OP_NULL)
180    goto skip;
181
182   rv2cv = cUNOPx(ex_list)->op_first;
183   if (!rv2cv)
184    goto skip;
185
186   while (1) {
187    OP *next = rv2cv->op_sibling;
188    if (!next)
189     break;
190    last_arg = rv2cv;
191    rv2cv    = next;
192   }
193
194   if (!(rv2cv->op_flags & OPf_KIDS))
195    goto skip;
196
197   gvop = cUNOPx(rv2cv)->op_first;
198   if (!gvop || gvop->op_type != OP_GV)
199    goto skip;
200
201   gv = cGVOPx_gv(gvop);
202
203   {
204    HV *stash = GvSTASH(gv);
205    SV *pp_sv, **svp;
206    CV *cv = NULL;
207    const char *name = GvNAME(gv);
208    I32         len  = GvNAMELEN(gv);
209    dMY_CXT;
210
211    svp = hv_fetch(MY_CXT.map, name, len, 0);
212    if (!svp)
213     goto skip;
214
215    pp_sv = *svp;
216    if (!pp_sv || !SvOK(pp_sv))
217     goto skip;
218
219    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
220     SvREFCNT_dec(cv);
221     GvCV(gv) = NULL;
222     if (!GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIO(gv) && !GvFORM(gv)) {
223      (void) hv_delete(stash, name, len, G_DISCARD);
224     }
225    }
226
227    o->op_type   = OP_CUSTOM;
228    o->op_ppaddr = INT2PTR(Perl_ppaddr_t, SvIVX(pp_sv));
229
230    if (last_arg)
231     last_arg->op_sibling = NULL;
232
233    op_free(rv2cv);
234
235    {
236     MAGIC *mg = mg_find(pp_sv, PERL_MAGIC_ext);
237     if (mg) {
238      sub_op_check_t check = INT2PTR(sub_op_check_t, SvIVX(mg->mg_obj));
239      o = CALL_FPTR(check)(aTHX_ o, mg->mg_ptr);
240     }
241    }
242
243    {
244     so_op_name_t *on = PerlMemShared_malloc(sizeof(*on) + len);
245     Copy(name, &on->buf, len, char);
246     (&on->buf)[len] = '\0';
247     on->len = len;
248 #ifdef USE_ITHREADS
249     MUTEX_LOCK(&so_op_name_mutex);
250 #endif /* USE_ITHREADS */
251     ptable_store(so_op_name, o, on);
252 #ifdef USE_ITHREADS
253     MUTEX_UNLOCK(&so_op_name_mutex);
254 #endif /* USE_ITHREADS */
255    }
256   }
257  }
258
259 skip:
260  return o;
261 }
262
263 STATIC OP *(*so_old_ck_gelem)(pTHX_ OP *) = 0;
264
265 STATIC OP *so_ck_gelem(pTHX_ OP *o) {
266  o = CALL_FPTR(so_old_ck_entersub)(aTHX_ o);
267
268  if (so_hint()) {
269   OP *rv2gv, *gvop;
270   GV *gv;
271
272   rv2gv = cUNOPo->op_first;
273   if (!rv2gv)
274    goto skip;
275
276   gvop = cUNOPx(rv2gv)->op_first;
277   if (!gvop || gvop->op_type != OP_GV)
278    goto skip;
279
280   gv = cGVOPx_gv(gvop);
281   if (!gv)
282    goto skip;
283
284   {
285    CV *cv;
286    dMY_CXT;
287
288    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
289     SvREFCNT_dec(cv);
290     GvCV(gv) = NULL;
291     if (!GvSV(gv) && !GvAV(gv) && !GvHV(gv) && !GvIO(gv) && !GvFORM(gv)) {
292      (void)hv_delete(GvSTASH(gv), GvNAME_get(gv), GvNAMELEN_get(gv), G_DISCARD);
293     }
294    }
295   }
296  }
297
298 skip:
299  return o;
300 }
301
302 /* --- XS ------------------------------------------------------------------ */
303
304 MODULE = Sub::Op      PACKAGE = Sub::Op
305
306 PROTOTYPES: ENABLE
307
308 BOOT:
309 {
310  so_op_name = ptable_new();
311 #ifdef USE_ITHREADS
312  MUTEX_INIT(&so_op_name_mutex);
313 #endif
314
315  MY_CXT_INIT;
316  MY_CXT.map         = newHV();
317  MY_CXT.placeholder = NULL;
318 #if SO_THREADSAFE
319  MY_CXT.owner       = aTHX;
320 #endif /* SO_THREADSAFE */
321
322  so_old_ck_entersub    = PL_check[OP_ENTERSUB];
323  PL_check[OP_ENTERSUB] = so_ck_entersub;
324  so_old_ck_gelem       = PL_check[OP_GELEM];
325  PL_check[OP_GELEM]    = so_ck_gelem;
326 }
327
328 #if SO_THREADSAFE
329
330 void
331 CLONE(...)
332 PROTOTYPE: DISABLE
333 PREINIT:
334  HV  *map;
335  CV  *placeholder;
336  tTHX owner;
337 CODE:
338  {
339   dMY_CXT;
340   owner       = MY_CXT.owner;
341   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
342   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
343  }
344  {
345   MY_CXT_CLONE;
346   MY_CXT.map         = map;
347   MY_CXT.placeholder = placeholder;
348   MY_CXT.owner       = aTHX;
349  }
350
351 #endif /* SO_THREADSAFE */
352
353 void
354 _placeholder(SV *sv)
355 PROTOTYPE: $
356 PPCODE:
357  if (SvROK(sv)) {
358   sv = SvRV(sv);
359   if (SvTYPE(sv) >= SVt_PVCV) {
360    dMY_CXT;
361    SvREFCNT_dec(MY_CXT.placeholder);
362    MY_CXT.placeholder = (CV *) SvREFCNT_inc(sv);
363   }
364  }
365  XSRETURN(0);
366
367 void
368 _custom_name(SV *op)
369 PROTOTYPE: $
370 PREINIT:
371  OP *o;
372  so_op_name_t *on;
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 #ifdef USE_ITHREADS
380  MUTEX_LOCK(&so_op_name_mutex);
381 #endif /* USE_ITHREADS */
382  on = ptable_fetch(so_op_name, o);
383 #ifdef USE_ITHREADS
384  MUTEX_UNLOCK(&so_op_name_mutex);
385 #endif /* USE_ITHREADS */
386  if (!on)
387   XSRETURN_UNDEF;
388  ST(0) = sv_2mortal(newSVpvn(&on->buf, on->len));
389  XSRETURN(1);