]> git.vpit.fr Git - perl/modules/Lexical-Types.git/blob - Types.xs
Newx is no longer needed
[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(const OP *o, SV *orig_pkg, SV *type_pkg, SV *type_meth, OP *(*pp_padsv)(pTHX)) {
78  lt_op_info *oi;
79
80 #ifdef USE_ITHREADS
81  MUTEX_LOCK(&lt_op_map_mutex);
82 #endif
83
84  if (!(oi = ptable_fetch(lt_op_map, o))) {
85   oi = PerlMemShared_malloc(sizeof *oi);
86   ptable_store(lt_op_map, o, oi);
87  }
88
89  oi->orig_pkg  = orig_pkg;
90  oi->type_pkg  = type_pkg;
91  oi->type_meth = type_meth;
92  oi->pp_padsv  = pp_padsv;
93
94 #ifdef USE_ITHREADS
95  MUTEX_UNLOCK(&lt_op_map_mutex);
96 #endif
97 }
98
99 STATIC const lt_op_info *lt_map_fetch(const OP *o, lt_op_info *oi) {
100  const lt_op_info *val;
101
102 #ifdef USE_ITHREADS
103  MUTEX_LOCK(&lt_op_map_mutex);
104 #endif
105
106  val = ptable_fetch(lt_op_map, o);
107  if (val) {
108   *oi = *val;
109   val = oi;
110  }
111
112 #ifdef USE_ITHREADS
113  MUTEX_UNLOCK(&lt_op_map_mutex);
114 #endif
115
116  return val;
117 }
118
119 /* --- Hooks --------------------------------------------------------------- */
120
121 /* ... Our pp_padsv ........................................................ */
122
123 STATIC OP *lt_pp_padsv(pTHX) {
124  lt_op_info oi;
125
126  if ((PL_op->op_private & OPpLVAL_INTRO) && lt_map_fetch(PL_op, &oi)) {
127   PADOFFSET targ = PL_op->op_targ;
128   SV *sv         = PAD_SVl(targ);
129
130   if (sv) {
131    int items;
132    dSP;
133
134    ENTER;
135    SAVETMPS;
136
137    PUSHMARK(SP);
138    EXTEND(SP, 3);
139    PUSHs(oi.type_pkg);
140    PUSHs(sv);
141    PUSHs(oi.orig_pkg);
142    PUTBACK;
143
144    items = call_sv(oi.type_meth, G_ARRAY | G_METHOD);
145
146    SPAGAIN;
147    switch (items) {
148     case 0:
149      break;
150     case 1:
151      sv_setsv(sv, POPs);
152      break;
153     default:
154      croak("Typed scalar initializer method should return zero or one scalar, but got %d", items);
155    }
156    PUTBACK;
157
158    FREETMPS;
159    LEAVE;
160   }
161
162   return CALL_FPTR(oi.pp_padsv)(aTHX);
163  }
164
165  return CALL_FPTR(PL_ppaddr[OP_PADSV])(aTHX);
166 }
167
168 STATIC OP *(*lt_pp_padsv_saved)(pTHX) = 0;
169
170 STATIC void lt_pp_padsv_save(void) {
171  if (lt_pp_padsv_saved)
172   return;
173
174  lt_pp_padsv_saved   = PL_ppaddr[OP_PADSV];
175  PL_ppaddr[OP_PADSV] = lt_pp_padsv;
176 }
177
178 STATIC void lt_pp_padsv_restore(OP *o) {
179  if (!lt_pp_padsv_saved)
180   return;
181
182  if (o->op_ppaddr == lt_pp_padsv)
183   o->op_ppaddr = lt_pp_padsv_saved;
184
185  PL_ppaddr[OP_PADSV] = lt_pp_padsv_saved;
186  lt_pp_padsv_saved   = 0;
187 }
188
189 /* ... Our ck_pad{any,sv} .................................................. */
190
191 /* Sadly, the PADSV OPs we are interested in don't trigger the padsv check
192  * function, but are instead manually mutated from a PADANY. This is why we set
193  * PL_ppaddr[OP_PADSV] in the padany check function so that PADSV OPs will have
194  * their pp_ppaddr set to our pp_padsv. PL_ppaddr[OP_PADSV] is then reset at the
195  * beginning of every ck_pad{any,sv}. Some unwanted OPs can still call our
196  * pp_padsv, but much less than if we would have set PL_ppaddr[OP_PADSV]
197  * globally. */
198
199 STATIC SV *lt_default_meth = NULL;
200
201 STATIC OP *(*lt_old_ck_padany)(pTHX_ OP *) = 0;
202
203 STATIC OP *lt_ck_padany(pTHX_ OP *o) {
204  HV *stash;
205  SV *hint;
206
207  lt_pp_padsv_restore(o);
208
209  o = CALL_FPTR(lt_old_ck_padany)(aTHX_ o);
210
211  stash = PL_in_my_stash;
212  if (stash && (hint = lt_hint())) {
213   SV *orig_pkg  = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash));
214   SV *orig_meth = lt_default_meth;
215   SV *type_pkg  = NULL;
216   SV *type_meth = NULL;
217   SV *code      = INT2PTR(SV *, SvUVX(hint));
218
219   SvREADONLY_on(orig_pkg);
220
221   if (code) {
222    int items;
223    dSP;
224
225    ENTER;
226    SAVETMPS;
227
228    PUSHMARK(SP);
229    EXTEND(SP, 2);
230    PUSHs(orig_pkg);
231    PUSHs(orig_meth);
232    PUTBACK;
233
234    items = call_sv(code, G_ARRAY);
235
236    SPAGAIN;
237    if (items > 2)
238     croak(__PACKAGE__ " mangler should return zero, one or two scalars, but got %d", items);
239    if (items == 0) {
240     SvREFCNT_dec(orig_pkg);
241     goto skip;
242    } else {
243     SV *rsv;
244     if (items > 1) {
245      rsv = POPs;
246      if (SvOK(rsv)) {
247       type_meth = newSVsv(rsv);
248       SvREADONLY_on(type_meth);
249      }
250     }
251     rsv = POPs;
252     if (SvOK(rsv)) {
253      type_pkg = newSVsv(rsv);
254      SvREADONLY_on(type_pkg);
255     }
256    }
257    PUTBACK;
258
259    FREETMPS;
260    LEAVE;
261   }
262
263   if (!type_pkg) {
264    type_pkg = orig_pkg;
265    SvREFCNT_inc(orig_pkg);
266   }
267
268   if (!type_meth) {
269    type_meth = orig_meth;
270    SvREFCNT_inc(orig_meth);
271   }
272
273   lt_pp_padsv_save();
274
275   lt_map_store(o, orig_pkg, type_pkg, type_meth, lt_pp_padsv_saved);
276  }
277
278 skip:
279  return o;
280 }
281
282 STATIC OP *(*lt_old_ck_padsv)(pTHX_ OP *) = 0;
283
284 STATIC OP *lt_ck_padsv(pTHX_ OP *o) {
285  lt_pp_padsv_restore(o);
286
287  return CALL_FPTR(lt_old_ck_padsv)(aTHX_ o);
288 }
289
290 STATIC U32 lt_initialized = 0;
291
292 /* --- XS ------------------------------------------------------------------ */
293
294 MODULE = Lexical::Types      PACKAGE = Lexical::Types
295
296 PROTOTYPES: DISABLE
297
298 BOOT: 
299 {                                    
300  if (!lt_initialized++) {
301   lt_op_map = ptable_new();
302 #ifdef USE_ITHREADS
303   MUTEX_INIT(&lt_op_map_mutex);
304 #endif
305
306   lt_default_meth = newSVpvn("TYPEDSCALAR", 11);
307   SvREADONLY_on(lt_default_meth);
308
309   PERL_HASH(lt_hash, __PACKAGE__, __PACKAGE_LEN__);
310
311   lt_old_ck_padany    = PL_check[OP_PADANY];
312   PL_check[OP_PADANY] = MEMBER_TO_FPTR(lt_ck_padany);
313   lt_old_ck_padsv     = PL_check[OP_PADSV];
314   PL_check[OP_PADSV]  = MEMBER_TO_FPTR(lt_ck_padsv);
315  }
316 }
317
318 SV *_tag(SV *ref)
319 PREINIT:
320  SV *ret;
321 CODE:
322  if (SvOK(ref) && SvROK(ref)) {
323   SV *sv = SvRV(ref);
324   SvREFCNT_inc(sv);
325   ret = newSVuv(PTR2UV(sv));
326  } else {
327   ret = newSVuv(0);
328  }
329  RETVAL = ret;
330 OUTPUT:
331  RETVAL