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