]> git.vpit.fr Git - perl/modules/Lexical-Types.git/blob - Types.xs
Fix building with PERL_IMPLICIT_SYS
[perl/modules/Lexical-Types.git] / Types.xs
1 /* This file is part of the Lexical-Types Perl module.
2  * See http://search.cpan.org/dist/Lexical-Types/ */
3
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8
9 #define __PACKAGE__     "Lexical::Types"
10 #define __PACKAGE_LEN__ (sizeof(__PACKAGE__)-1)
11
12 /* --- Compatibility wrappers ---------------------------------------------- */
13
14 #define LT_HAS_PERL(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
15
16 #if LT_HAS_PERL(5, 10, 0) || defined(PL_parser)
17 # ifndef PL_in_my_stash
18 #  define PL_in_my_stash PL_parser->in_my_stash
19 # endif
20 #else
21 # ifndef PL_in_my_stash
22 #  define PL_in_my_stash PL_Iin_my_stash
23 # endif
24 #endif
25
26 #ifndef HvNAME_get
27 # define HvNAME_get(H) HvNAME(H)
28 #endif
29
30 #ifndef HvNAMELEN_get
31 # define HvNAMELEN_get(H) strlen(HvNAME_get(H))
32 #endif
33
34 /* --- Helpers ------------------------------------------------------------- */
35
36 /* ... Hints ............................................................... */
37
38 STATIC U32 lt_hash = 0;
39
40 STATIC SV *lt_hint(pTHX) {
41 #define lt_hint() lt_hint(aTHX)
42  SV *id;
43 #if LT_HAS_PERL(5, 10, 0)
44  id = Perl_refcounted_he_fetch(aTHX_ PL_curcop->cop_hints_hash,
45                                      NULL,
46                                      __PACKAGE__, __PACKAGE_LEN__,
47                                      0,
48                                      lt_hash);
49 #else
50  SV **val = hv_fetch(GvHV(PL_hintgv), __PACKAGE__, __PACKAGE_LEN__, lt_hash);
51  if (!val)
52   return 0;
53  id = *val;
54 #endif
55  return (id && SvOK(id)) ? id : NULL;
56 }
57
58 /* ... op => info map ...................................................... */
59
60 #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
61
62 #include "ptable.h"
63
64 STATIC ptable *lt_op_map = NULL;
65
66 #ifdef USE_ITHREADS
67 STATIC perl_mutex lt_op_map_mutex;
68 #endif
69
70 typedef struct {
71  SV *orig_pkg;
72  SV *type_pkg;
73  SV *type_meth;
74  OP *(*pp_padsv)(pTHX);
75 } lt_op_info;
76
77 STATIC void lt_map_store(pPTABLE_ const OP *o, SV *orig_pkg, SV *type_pkg, SV *type_meth, OP *(*pp_padsv)(pTHX)) {
78 #define lt_map_store(O, OP, TP, TM, PP) lt_map_store(aPTABLE_ (O), (OP), (TP), (TM), (PP))
79  lt_op_info *oi;
80
81 #ifdef USE_ITHREADS
82  MUTEX_LOCK(&lt_op_map_mutex);
83 #endif
84
85  if (!(oi = ptable_fetch(lt_op_map, o))) {
86   oi = PerlMemShared_malloc(sizeof *oi);
87   ptable_store(lt_op_map, o, oi);
88  }
89
90  oi->orig_pkg  = orig_pkg;
91  oi->type_pkg  = type_pkg;
92  oi->type_meth = type_meth;
93  oi->pp_padsv  = pp_padsv;
94
95 #ifdef USE_ITHREADS
96  MUTEX_UNLOCK(&lt_op_map_mutex);
97 #endif
98 }
99
100 STATIC const lt_op_info *lt_map_fetch(const OP *o, lt_op_info *oi) {
101  const lt_op_info *val;
102
103 #ifdef USE_ITHREADS
104  MUTEX_LOCK(&lt_op_map_mutex);
105 #endif
106
107  val = ptable_fetch(lt_op_map, o);
108  if (val) {
109   *oi = *val;
110   val = oi;
111  }
112
113 #ifdef USE_ITHREADS
114  MUTEX_UNLOCK(&lt_op_map_mutex);
115 #endif
116
117  return val;
118 }
119
120 /* --- Hooks --------------------------------------------------------------- */
121
122 /* ... Our pp_padsv ........................................................ */
123
124 STATIC OP *lt_pp_padsv(pTHX) {
125  lt_op_info oi;
126
127  if ((PL_op->op_private & OPpLVAL_INTRO) && lt_map_fetch(PL_op, &oi)) {
128   PADOFFSET targ = PL_op->op_targ;
129   SV *sv         = PAD_SVl(targ);
130
131   if (sv) {
132    int items;
133    dSP;
134
135    ENTER;
136    SAVETMPS;
137
138    PUSHMARK(SP);
139    EXTEND(SP, 3);
140    PUSHs(oi.type_pkg);
141    PUSHs(sv);
142    PUSHs(oi.orig_pkg);
143    PUTBACK;
144
145    items = call_sv(oi.type_meth, G_ARRAY | G_METHOD);
146
147    SPAGAIN;
148    switch (items) {
149     case 0:
150      break;
151     case 1:
152      sv_setsv(sv, POPs);
153      break;
154     default:
155      croak("Typed scalar initializer method should return zero or one scalar, but got %d", items);
156    }
157    PUTBACK;
158
159    FREETMPS;
160    LEAVE;
161   }
162
163   return CALL_FPTR(oi.pp_padsv)(aTHX);
164  }
165
166  return CALL_FPTR(PL_ppaddr[OP_PADSV])(aTHX);
167 }
168
169 STATIC OP *(*lt_pp_padsv_saved)(pTHX) = 0;
170
171 STATIC void lt_pp_padsv_save(void) {
172  if (lt_pp_padsv_saved)
173   return;
174
175  lt_pp_padsv_saved   = PL_ppaddr[OP_PADSV];
176  PL_ppaddr[OP_PADSV] = lt_pp_padsv;
177 }
178
179 STATIC void lt_pp_padsv_restore(OP *o) {
180  if (!lt_pp_padsv_saved)
181   return;
182
183  if (o->op_ppaddr == lt_pp_padsv)
184   o->op_ppaddr = lt_pp_padsv_saved;
185
186  PL_ppaddr[OP_PADSV] = lt_pp_padsv_saved;
187  lt_pp_padsv_saved   = 0;
188 }
189
190 /* ... Our ck_pad{any,sv} .................................................. */
191
192 /* Sadly, the PADSV OPs we are interested in don't trigger the padsv check
193  * function, but are instead manually mutated from a PADANY. This is why we set
194  * PL_ppaddr[OP_PADSV] in the padany check function so that PADSV OPs will have
195  * their pp_ppaddr set to our pp_padsv. PL_ppaddr[OP_PADSV] is then reset at the
196  * beginning of every ck_pad{any,sv}. Some unwanted OPs can still call our
197  * pp_padsv, but much less than if we would have set PL_ppaddr[OP_PADSV]
198  * globally. */
199
200 STATIC SV *lt_default_meth = NULL;
201
202 STATIC OP *(*lt_old_ck_padany)(pTHX_ OP *) = 0;
203
204 STATIC OP *lt_ck_padany(pTHX_ OP *o) {
205  HV *stash;
206  SV *hint;
207
208  lt_pp_padsv_restore(o);
209
210  o = CALL_FPTR(lt_old_ck_padany)(aTHX_ o);
211
212  stash = PL_in_my_stash;
213  if (stash && (hint = lt_hint())) {
214   SV *orig_pkg  = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash));
215   SV *orig_meth = lt_default_meth;
216   SV *type_pkg  = NULL;
217   SV *type_meth = NULL;
218   SV *code      = INT2PTR(SV *, SvUVX(hint));
219
220   SvREADONLY_on(orig_pkg);
221
222   if (code) {
223    int items;
224    dSP;
225
226    ENTER;
227    SAVETMPS;
228
229    PUSHMARK(SP);
230    EXTEND(SP, 2);
231    PUSHs(orig_pkg);
232    PUSHs(orig_meth);
233    PUTBACK;
234
235    items = call_sv(code, G_ARRAY);
236
237    SPAGAIN;
238    if (items > 2)
239     croak(__PACKAGE__ " mangler should return zero, one or two scalars, but got %d", items);
240    if (items == 0) {
241     SvREFCNT_dec(orig_pkg);
242     goto skip;
243    } else {
244     SV *rsv;
245     if (items > 1) {
246      rsv = POPs;
247      if (SvOK(rsv)) {
248       type_meth = newSVsv(rsv);
249       SvREADONLY_on(type_meth);
250      }
251     }
252     rsv = POPs;
253     if (SvOK(rsv)) {
254      type_pkg = newSVsv(rsv);
255      SvREADONLY_on(type_pkg);
256     }
257    }
258    PUTBACK;
259
260    FREETMPS;
261    LEAVE;
262   }
263
264   if (!type_pkg) {
265    type_pkg = orig_pkg;
266    SvREFCNT_inc(orig_pkg);
267   }
268
269   if (!type_meth) {
270    type_meth = orig_meth;
271    SvREFCNT_inc(orig_meth);
272   }
273
274   lt_pp_padsv_save();
275
276   lt_map_store(o, orig_pkg, type_pkg, type_meth, lt_pp_padsv_saved);
277  }
278
279 skip:
280  return o;
281 }
282
283 STATIC OP *(*lt_old_ck_padsv)(pTHX_ OP *) = 0;
284
285 STATIC OP *lt_ck_padsv(pTHX_ OP *o) {
286  lt_pp_padsv_restore(o);
287
288  return CALL_FPTR(lt_old_ck_padsv)(aTHX_ o);
289 }
290
291 STATIC U32 lt_initialized = 0;
292
293 /* --- XS ------------------------------------------------------------------ */
294
295 MODULE = Lexical::Types      PACKAGE = Lexical::Types
296
297 PROTOTYPES: DISABLE
298
299 BOOT: 
300 {                                    
301  if (!lt_initialized++) {
302   lt_op_map = ptable_new();
303 #ifdef USE_ITHREADS
304   MUTEX_INIT(&lt_op_map_mutex);
305 #endif
306
307   lt_default_meth = newSVpvn("TYPEDSCALAR", 11);
308   SvREADONLY_on(lt_default_meth);
309
310   PERL_HASH(lt_hash, __PACKAGE__, __PACKAGE_LEN__);
311
312   lt_old_ck_padany    = PL_check[OP_PADANY];
313   PL_check[OP_PADANY] = MEMBER_TO_FPTR(lt_ck_padany);
314   lt_old_ck_padsv     = PL_check[OP_PADSV];
315   PL_check[OP_PADSV]  = MEMBER_TO_FPTR(lt_ck_padsv);
316  }
317 }
318
319 SV *_tag(SV *ref)
320 PREINIT:
321  SV *ret;
322 CODE:
323  if (SvOK(ref) && SvROK(ref)) {
324   SV *sv = SvRV(ref);
325   SvREFCNT_inc(sv);
326   ret = newSVuv(PTR2UV(sv));
327  } else {
328   ret = newSVuv(0);
329  }
330  RETVAL = ret;
331 OUTPUT:
332  RETVAL