]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
Test monkeypatching
[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    }
223
224    o->op_type   = OP_CUSTOM;
225    o->op_ppaddr = INT2PTR(Perl_ppaddr_t, SvIVX(pp_sv));
226
227    if (last_arg)
228     last_arg->op_sibling = NULL;
229
230    op_free(rv2cv);
231
232    {
233     MAGIC *mg = mg_find(pp_sv, PERL_MAGIC_ext);
234     if (mg) {
235      sub_op_check_t check = INT2PTR(sub_op_check_t, SvIVX(mg->mg_obj));
236      o = CALL_FPTR(check)(aTHX_ o, mg->mg_ptr);
237     }
238    }
239
240    {
241     so_op_name_t *on = PerlMemShared_malloc(sizeof(*on) + len);
242     Copy(name, &on->buf, len, char);
243     (&on->buf)[len] = '\0';
244     on->len = len;
245 #ifdef USE_ITHREADS
246     MUTEX_LOCK(&so_op_name_mutex);
247 #endif /* USE_ITHREADS */
248     ptable_store(so_op_name, o, on);
249 #ifdef USE_ITHREADS
250     MUTEX_UNLOCK(&so_op_name_mutex);
251 #endif /* USE_ITHREADS */
252    }
253   }
254  }
255
256 skip:
257  return o;
258 }
259
260 STATIC OP *(*so_old_ck_gelem)(pTHX_ OP *) = 0;
261
262 STATIC OP *so_ck_gelem(pTHX_ OP *o) {
263  o = CALL_FPTR(so_old_ck_entersub)(aTHX_ o);
264
265  if (so_hint()) {
266   OP *rv2gv, *gvop;
267   GV *gv;
268
269   rv2gv = cUNOPo->op_first;
270   if (!rv2gv)
271    goto skip;
272
273   gvop = cUNOPx(rv2gv)->op_first;
274   if (!gvop || gvop->op_type != OP_GV)
275    goto skip;
276
277   gv = cGVOPx_gv(gvop);
278   if (!gv)
279    goto skip;
280
281   {
282    CV *cv;
283    dMY_CXT;
284
285    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
286     SvREFCNT_dec(cv);
287     GvCV(gv) = NULL;
288    }
289   }
290  }
291
292 skip:
293  return o;
294 }
295
296 /* --- XS ------------------------------------------------------------------ */
297
298 MODULE = Sub::Op      PACKAGE = Sub::Op
299
300 PROTOTYPES: ENABLE
301
302 BOOT:
303 {
304  so_op_name = ptable_new();
305 #ifdef USE_ITHREADS
306  MUTEX_INIT(&so_op_name_mutex);
307 #endif
308
309  MY_CXT_INIT;
310  MY_CXT.map         = newHV();
311  MY_CXT.placeholder = NULL;
312 #if SO_THREADSAFE
313  MY_CXT.owner       = aTHX;
314 #endif /* SO_THREADSAFE */
315
316  so_old_ck_entersub    = PL_check[OP_ENTERSUB];
317  PL_check[OP_ENTERSUB] = so_ck_entersub;
318  so_old_ck_gelem       = PL_check[OP_GELEM];
319  PL_check[OP_GELEM]    = so_ck_gelem;
320 }
321
322 #if SO_THREADSAFE
323
324 void
325 CLONE(...)
326 PROTOTYPE: DISABLE
327 PREINIT:
328  HV  *map;
329  CV  *placeholder;
330  tTHX owner;
331 CODE:
332  {
333   dMY_CXT;
334   owner       = MY_CXT.owner;
335   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
336   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
337  }
338  {
339   MY_CXT_CLONE;
340   MY_CXT.map         = map;
341   MY_CXT.placeholder = placeholder;
342   MY_CXT.owner       = aTHX;
343  }
344
345 #endif /* SO_THREADSAFE */
346
347 void
348 _placeholder(SV *sv)
349 PROTOTYPE: $
350 PPCODE:
351  if (SvROK(sv)) {
352   sv = SvRV(sv);
353   if (SvTYPE(sv) >= SVt_PVCV) {
354    dMY_CXT;
355    SvREFCNT_dec(MY_CXT.placeholder);
356    MY_CXT.placeholder = (CV *) SvREFCNT_inc(sv);
357   }
358  }
359  XSRETURN(0);
360
361 void
362 _custom_name(SV *op)
363 PROTOTYPE: $
364 PREINIT:
365  OP *o;
366  so_op_name_t *on;
367 PPCODE:
368  if (!SvROK(op))
369   XSRETURN_UNDEF;
370  o = INT2PTR(OP *, SvIV(SvRV(op)));
371  if (!o || o->op_type != OP_CUSTOM)
372   XSRETURN_UNDEF;
373 #ifdef USE_ITHREADS
374  MUTEX_LOCK(&so_op_name_mutex);
375 #endif /* USE_ITHREADS */
376  on = ptable_fetch(so_op_name, o);
377 #ifdef USE_ITHREADS
378  MUTEX_UNLOCK(&so_op_name_mutex);
379 #endif /* USE_ITHREADS */
380  if (!on)
381   XSRETURN_UNDEF;
382  ST(0) = sv_2mortal(newSVpvn(&on->buf, on->len));
383  XSRETURN(1);