]> git.vpit.fr Git - perl/modules/Sub-Op.git/blob - Op.xs
Handle existing constant subs
[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    SV *pp_sv, **svp;
205    CV *cv = NULL;
206    const char *name = GvNAME(gv);
207    I32         len  = GvNAMELEN(gv);
208    dMY_CXT;
209
210    svp = hv_fetch(MY_CXT.map, name, len, 0);
211    if (!svp)
212     goto skip;
213
214    pp_sv = *svp;
215    if (!pp_sv || !SvOK(pp_sv))
216     goto skip;
217
218    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
219     SvREFCNT_dec(cv);
220     GvCV(gv) = NULL;
221    }
222
223    o->op_type   = OP_CUSTOM;
224    o->op_ppaddr = INT2PTR(Perl_ppaddr_t, SvIVX(pp_sv));
225
226    if (last_arg)
227     last_arg->op_sibling = NULL;
228
229    op_free(rv2cv);
230
231    {
232     MAGIC *mg = mg_find(pp_sv, PERL_MAGIC_ext);
233     if (mg) {
234      sub_op_check_t check = INT2PTR(sub_op_check_t, SvIVX(mg->mg_obj));
235      o = CALL_FPTR(check)(aTHX_ o, mg->mg_ptr);
236     }
237    }
238
239    {
240     so_op_name_t *on = PerlMemShared_malloc(sizeof(*on) + len);
241     Copy(name, &on->buf, len, char);
242     (&on->buf)[len] = '\0';
243     on->len = len;
244 #ifdef USE_ITHREADS
245     MUTEX_LOCK(&so_op_name_mutex);
246 #endif /* USE_ITHREADS */
247     ptable_store(so_op_name, o, on);
248 #ifdef USE_ITHREADS
249     MUTEX_UNLOCK(&so_op_name_mutex);
250 #endif /* USE_ITHREADS */
251    }
252   }
253  }
254
255 skip:
256  return o;
257 }
258
259 STATIC OP *(*so_old_ck_gelem)(pTHX_ OP *) = 0;
260
261 STATIC OP *so_ck_gelem(pTHX_ OP *o) {
262  o = CALL_FPTR(so_old_ck_entersub)(aTHX_ o);
263
264  if (so_hint()) {
265   OP *rv2gv, *gvop;
266   GV *gv;
267
268   rv2gv = cUNOPo->op_first;
269   if (!rv2gv)
270    goto skip;
271
272   gvop = cUNOPx(rv2gv)->op_first;
273   if (!gvop || gvop->op_type != OP_GV)
274    goto skip;
275
276   gv = cGVOPx_gv(gvop);
277   if (!gv)
278    goto skip;
279
280   {
281    CV *cv;
282    dMY_CXT;
283
284    if (gv && SvTYPE(gv) >= SVt_PVGV && (cv = GvCV(gv)) == MY_CXT.placeholder) {
285     SvREFCNT_dec(cv);
286     GvCV(gv) = NULL;
287    }
288   }
289  }
290
291 skip:
292  return o;
293 }
294
295 /* --- XS ------------------------------------------------------------------ */
296
297 MODULE = Sub::Op      PACKAGE = Sub::Op
298
299 PROTOTYPES: ENABLE
300
301 BOOT:
302 {
303  so_op_name = ptable_new();
304 #ifdef USE_ITHREADS
305  MUTEX_INIT(&so_op_name_mutex);
306 #endif
307
308  MY_CXT_INIT;
309  MY_CXT.map         = newHV();
310  MY_CXT.placeholder = NULL;
311 #if SO_THREADSAFE
312  MY_CXT.owner       = aTHX;
313 #endif /* SO_THREADSAFE */
314
315  so_old_ck_entersub    = PL_check[OP_ENTERSUB];
316  PL_check[OP_ENTERSUB] = so_ck_entersub;
317  so_old_ck_gelem       = PL_check[OP_GELEM];
318  PL_check[OP_GELEM]    = so_ck_gelem;
319 }
320
321 #if SO_THREADSAFE
322
323 void
324 CLONE(...)
325 PROTOTYPE: DISABLE
326 PREINIT:
327  HV  *map;
328  CV  *placeholder;
329  tTHX owner;
330 PPCODE:
331  {
332   dMY_CXT;
333   owner       = MY_CXT.owner;
334   map         = (HV *) so_clone((SV *) MY_CXT.map,         owner);
335   placeholder = (CV *) so_clone((SV *) MY_CXT.placeholder, owner);
336  }
337  {
338   MY_CXT_CLONE;
339   MY_CXT.map         = map;
340   MY_CXT.placeholder = placeholder;
341   MY_CXT.owner       = aTHX;
342  }
343  XSRETURN(0);
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);
384
385 void
386 _constant_sub(SV *sv)
387 PROTOTYPE: $
388 PPCODE:
389  if (!SvROK(sv))
390   XSRETURN_UNDEF;
391  sv = SvRV(sv);
392  if (SvTYPE(sv) < SVt_PVCV)
393   XSRETURN_UNDEF;
394  ST(0) = sv_2mortal(newSVuv(CvCONST(sv)));
395  XSRETURN(1);