2168bb5ec242f84ddbe9b4aaae2dc099687ced38
[gitmo/Class-MOP.git] / MOP.xs
1 /* There's a lot of cases of doubled parens in here like this:
2
3   while ( (he = ...) ) {
4
5 This shuts up warnings from gcc -Wall
6 */
7
8 #include "EXTERN.h"
9 #include "perl.h"
10 #include "XSUB.h"
11
12 #define NEED_newRV_noinc
13 #define NEED_sv_2pv_flags
14 #define NEED_sv_2pv_nolen
15 #include "ppport.h"
16
17 SV *key_name;
18 U32 hash_name;
19
20 SV *key_package;
21 U32 hash_package;
22
23 SV *key_package_name;
24 U32 hash_package_name;
25
26 SV *key_body;
27 U32 hash_body;
28
29 SV *key_package_cache_flag;
30 U32 hash_package_cache_flag;
31
32 SV *key_methods;
33 U32 hash_methods;
34
35 SV *method_metaclass;
36 SV *associated_metaclass;
37 SV *wrap;
38
39
40 #define check_package_cache_flag(stash) mop_check_package_cache_flag(aTHX_ stash)
41 #if PERL_VERSION >= 10
42
43 static UV
44 mop_check_package_cache_flag(pTHX_ HV* stash) {
45     assert(SvTYPE(stash) == SVt_PVHV);
46
47     /* here we're trying to implement a c version of mro::get_pkg_gen($stash),
48      * however the perl core doesn't make it easy for us. It doesn't provide an
49      * api that just does what we want.
50      *
51      * However, we know that the information we want is, inside the core,
52      * available using HvMROMETA(stash)->pkg_gen. Unfortunately, although the
53      * HvMROMETA macro is public, it is implemented using Perl_mro_meta_init,
54      * which is not public and only available inside the core, as the mro
55      * interface as well as the structure returned by mro_meta_init isn't
56      * considered to be stable yet.
57      *
58      * Perl_mro_meta_init isn't declared static, so we could just define it
59      * ourselfs if perls headers don't do that for us, except that won't work
60      * on platforms where symbols need to be explicitly exported when linking
61      * shared libraries.
62      *
63      * So our, hopefully temporary, solution is to be even more evil and
64      * basically reimplement HvMROMETA in a very fragile way that'll blow up
65      * when the relevant parts of the mro implementation in core change.
66      *
67      * :-(
68      *
69      */
70
71     return HvAUX(stash)->xhv_mro_meta
72          ? HvAUX(stash)->xhv_mro_meta->pkg_gen
73          : 0;
74 }
75
76 #else /* pre 5.10.0 */
77
78 static UV
79 mop_check_package_cache_flag(pTHX_ HV *stash) {
80     PERL_UNUSED_ARG(stash);
81     assert(SvTYPE(stash) == SVt_PVHV);
82
83     return PL_sub_generation;
84 }
85 #endif
86
87 #define call0(s, m)  mop_call0(aTHX_ s, m)
88 static SV *
89 mop_call0(pTHX_ SV *const self, SV *const method) {
90     dSP;
91     SV *ret;
92
93     PUSHMARK(SP);
94     XPUSHs(self);
95     PUTBACK;
96
97     call_sv(method, G_SCALAR | G_METHOD);
98
99     SPAGAIN;
100     ret = POPs;
101     PUTBACK;
102
103     return ret;
104 }
105
106 static int
107 get_code_info (SV *coderef, char **pkg, char **name)
108 {
109     if (!SvOK(coderef) || !SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
110         return 0;
111     }
112
113     coderef = SvRV(coderef);
114     /* I think this only gets triggered with a mangled coderef, but if
115        we hit it without the guard, we segfault. The slightly odd return
116        value strikes me as an improvement (mst)
117     */
118 #ifdef isGV_with_GP
119     if ( isGV_with_GP(CvGV(coderef)) ) {
120 #endif
121         *pkg     = HvNAME( GvSTASH(CvGV(coderef)) );
122         *name    = GvNAME( CvGV(coderef) );
123 #ifdef isGV_with_GP
124     } else {
125         *pkg     = "__UNKNOWN__";
126         *name    = "__ANON__";
127     }
128 #endif
129
130     return 1;
131 }
132
133 typedef enum {
134     TYPE_FILTER_NONE,
135     TYPE_FILTER_CODE,
136     TYPE_FILTER_ARRAY,
137     TYPE_FILTER_IO,
138     TYPE_FILTER_HASH,
139     TYPE_FILTER_SCALAR,
140 } type_filter_t;
141
142 static HV *
143 get_all_package_symbols(HV *stash, type_filter_t filter)
144 {
145     HE *he;
146     HV *ret = newHV();
147
148     (void)hv_iterinit(stash);
149
150     if (filter == TYPE_FILTER_NONE) {
151         while ( (he = hv_iternext(stash)) ) {
152             STRLEN keylen;
153             char *key = HePV(he, keylen);
154             if (!hv_store(ret, key, keylen, SvREFCNT_inc(HeVAL(he)), 0)) {
155                 croak("failed to store glob ref");
156             }
157         }
158
159         return ret;
160     }
161
162     while ( (he = hv_iternext(stash)) ) {
163         SV *const gv = HeVAL(he);
164         SV *sv = NULL;
165         char *key;
166         STRLEN keylen;
167         char *package;
168         SV *fq;
169
170         switch( SvTYPE(gv) ) {
171 #ifndef SVt_RV
172             case SVt_RV:
173 #endif
174             case SVt_PV:
175             case SVt_IV:
176                 /* expand the gv into a real typeglob if it
177                  * contains stub functions and we were asked to
178                  * return CODE symbols */
179                 if (filter == TYPE_FILTER_CODE) {
180                     if (SvROK(gv)) {
181                         /* we don't really care about the length,
182                            but that's the API */
183                         key = HePV(he, keylen);
184                         package = HvNAME(stash);
185                         fq = newSVpvf("%s::%s", package, key);
186                         sv = (SV *)get_cv(SvPV_nolen(fq), 0);
187                         break;
188                     }
189
190                     key = HePV(he, keylen);
191                     gv_init((GV *)gv, stash, key, keylen, GV_ADDMULTI);
192                 }
193                 /* fall through */
194             case SVt_PVGV:
195                 switch (filter) {
196                     case TYPE_FILTER_CODE:   sv = (SV *)GvCVu(gv); break;
197                     case TYPE_FILTER_ARRAY:  sv = (SV *)GvAV(gv);  break;
198                     case TYPE_FILTER_IO:     sv = (SV *)GvIO(gv);  break;
199                     case TYPE_FILTER_HASH:   sv = (SV *)GvHV(gv);  break;
200                     case TYPE_FILTER_SCALAR: sv = (SV *)GvSV(gv);  break;
201                     default:
202                         croak("Unknown type");
203                 }
204                 break;
205             default:
206                 continue;
207         }
208
209         if (sv) {
210             char *key = HePV(he, keylen);
211             if (!hv_store(ret, key, keylen, newRV_inc(sv), 0)) {
212                 croak("failed to store symbol ref");
213             }
214         }
215     }
216
217     return ret;
218 }
219
220
221 static void
222 mop_update_method_map(pTHX_ SV *const self, SV *const class_name, HV *const stash, HV *const map) {
223     const char *const class_name_pv = HvNAME(stash); /* must be HvNAME(stash), not SvPV_nolen_const(class_name) */
224     SV   *method_metaclass_name;
225     char *method_name;
226     I32   method_name_len;
227     SV   *coderef;
228     HV   *symbols;
229     dSP;
230
231     symbols = get_all_package_symbols(stash, TYPE_FILTER_CODE);
232
233     (void)hv_iterinit(symbols);
234     while ( (coderef = hv_iternextsv(symbols, &method_name, &method_name_len)) ) {
235         CV *cv = (CV *)SvRV(coderef);
236         char *cvpkg_name;
237         char *cv_name;
238         SV *method_slot;
239         SV *method_object;
240
241         if (!get_code_info(coderef, &cvpkg_name, &cv_name)) {
242             continue;
243         }
244
245         /* this checks to see that the subroutine is actually from our package  */
246         if ( !(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__")) ) {
247             if ( strNE(cvpkg_name, class_name_pv) ) {
248                 continue;
249             }
250         }
251
252         method_slot = *hv_fetch(map, method_name, method_name_len, TRUE);
253         if ( SvOK(method_slot) ) {
254             SV *const body = call0(method_slot, key_body); /* $method_object->body() */
255             if ( SvROK(body) && ((CV *) SvRV(body)) == cv ) {
256                 continue;
257             }
258         }
259
260         method_metaclass_name = call0(self, method_metaclass); /* $self->method_metaclass() */
261
262         /*
263             $method_object = $method_metaclass->wrap(
264                 $cv,
265                 associated_metaclass => $self,
266                 package_name         => $class_name,
267                 name                 => $method_name
268             );
269         */
270         ENTER;
271         SAVETMPS;
272
273         PUSHMARK(SP);
274         EXTEND(SP, 8);
275         PUSHs(method_metaclass_name); /* invocant */
276         mPUSHs(newRV_inc((SV *)cv));
277         PUSHs(associated_metaclass);
278         PUSHs(self);
279         PUSHs(key_package_name);
280         PUSHs(class_name);
281         PUSHs(key_name);
282         mPUSHs(newSVpv(method_name, method_name_len));
283         PUTBACK;
284
285         call_sv(wrap, G_SCALAR | G_METHOD);
286         SPAGAIN;
287         method_object = POPs;
288         PUTBACK;
289         /* $map->{$method_name} = $method_object */
290         sv_setsv(method_slot, method_object);
291
292         FREETMPS;
293         LEAVE;
294     }
295 }
296
297 /*
298 get_code_info:
299   Pass in a coderef, returns:
300   [ $pkg_name, $coderef_name ] ie:
301   [ 'Foo::Bar', 'new' ]
302 */
303
304 MODULE = Class::MOP   PACKAGE = Class::MOP
305
306 BOOT:
307     key_name = newSVpvs("name");
308     key_body = newSVpvs("body");
309     key_package = newSVpvs("package");
310     key_package_name = newSVpvs("package_name");
311     key_package_cache_flag = newSVpvs("_package_cache_flag");
312     key_methods = newSVpvs("methods");
313
314     PERL_HASH(hash_name, "name", 4);
315     PERL_HASH(hash_body, "body", 4);
316     PERL_HASH(hash_package, "package", 7);
317     PERL_HASH(hash_package_name, "package_name", 12);
318     PERL_HASH(hash_package_cache_flag, "_package_cache_flag", 19);
319     PERL_HASH(hash_methods, "methods", 7);
320
321     method_metaclass     = newSVpvs("method_metaclass");
322     wrap                 = newSVpvs("wrap");
323     associated_metaclass = newSVpvs("associated_metaclass");
324
325
326 PROTOTYPES: ENABLE
327
328
329 void
330 get_code_info(coderef)
331     SV *coderef
332     PREINIT:
333         char *pkg  = NULL;
334         char *name = NULL;
335     PPCODE:
336         if (get_code_info(coderef, &pkg, &name)) {
337             EXTEND(SP, 2);
338             PUSHs(newSVpv(pkg, 0));
339             PUSHs(newSVpv(name, 0));
340         }
341
342
343 MODULE = Class::MOP   PACKAGE = Class::MOP::Package
344
345 void
346 get_all_package_symbols(self, filter=TYPE_FILTER_NONE)
347     SV *self
348     type_filter_t filter
349     PROTOTYPE: $;$
350     PREINIT:
351         HV *stash = NULL;
352         HV *symbols = NULL;
353         register HE *he;
354     PPCODE:
355         if ( ! SvROK(self) ) {
356             die("Cannot call get_all_package_symbols as a class method");
357         }
358
359         if (GIMME_V == G_VOID) {
360             XSRETURN_EMPTY;
361         }
362
363
364         PUTBACK;
365
366         if ( (he = hv_fetch_ent((HV *)SvRV(self), key_package, 0, hash_package)) ) {
367             stash = gv_stashsv(HeVAL(he), 0);
368         }
369
370
371         if (!stash) {
372             switch (GIMME_V) {
373                 case G_SCALAR: XSRETURN_UNDEF; break;
374                 case G_ARRAY:  XSRETURN_EMPTY; break;
375             }
376         }
377
378         symbols = get_all_package_symbols(stash, filter);
379
380         switch (GIMME_V) {
381             case G_SCALAR:
382                 PUSHs(sv_2mortal(newRV_inc((SV *)symbols)));
383                 break;
384             case G_ARRAY:
385                 warn("Class::MOP::Package::get_all_package_symbols in list context is deprecated. use scalar context instead.");
386
387                 EXTEND(SP, HvKEYS(symbols) * 2);
388
389                 while ((he = hv_iternext(symbols))) {
390                     PUSHs(hv_iterkeysv(he));
391                     PUSHs(sv_2mortal(SvREFCNT_inc(HeVAL(he))));
392                 }
393
394                 break;
395             default:
396                 break;
397         }
398
399         SvREFCNT_dec((SV *)symbols);
400
401 void
402 name(self)
403     SV *self
404     PREINIT:
405         register HE *he;
406     PPCODE:
407         if ( ! SvROK(self) ) {
408             die("Cannot call name as a class method");
409         }
410
411         if ( (he = hv_fetch_ent((HV *)SvRV(self), key_package, 0, hash_package)) )
412             XPUSHs(HeVAL(he));
413         else
414             ST(0) = &PL_sv_undef;
415
416 MODULE = Class::MOP   PACKAGE = Class::MOP::Attribute
417
418 void
419 name(self)
420     SV *self
421     PREINIT:
422         register HE *he;
423     PPCODE:
424         if ( ! SvROK(self) ) {
425             die("Cannot call name as a class method");
426         }
427
428         if ( (he = hv_fetch_ent((HV *)SvRV(self), key_name, 0, hash_name)) )
429             XPUSHs(HeVAL(he));
430         else
431             ST(0) = &PL_sv_undef;
432
433 MODULE = Class::MOP   PACKAGE = Class::MOP::Method
434
435 void
436 name(self)
437     SV *self
438     PREINIT:
439         register HE *he;
440     PPCODE:
441         if ( ! SvROK(self) ) {
442             die("Cannot call name as a class method");
443         }
444
445         if ( (he = hv_fetch_ent((HV *)SvRV(self), key_name, 0, hash_name)) )
446             XPUSHs(HeVAL(he));
447         else
448             ST(0) = &PL_sv_undef;
449
450 void
451 package_name(self)
452     SV *self
453     PREINIT:
454         register HE *he;
455     PPCODE:
456         if ( ! SvROK(self) ) {
457             die("Cannot call package_name as a class method");
458         }
459
460         if ( (he = hv_fetch_ent((HV *)SvRV(self), key_package_name, 0, hash_package_name)) )
461             XPUSHs(HeVAL(he));
462         else
463             ST(0) = &PL_sv_undef;
464
465 void
466 body(self)
467     SV *self
468     PREINIT:
469         register HE *he;
470     PPCODE:
471         if ( ! SvROK(self) ) {
472             die("Cannot call body as a class method");
473         }
474
475         if ( (he = hv_fetch_ent((HV *)SvRV(self), key_body, 0, hash_body)) )
476             XPUSHs(HeVAL(he));
477         else
478             ST(0) = &PL_sv_undef;
479
480
481 MODULE = Class::MOP    PACKAGE = Class::MOP::Class
482
483 void
484 get_method_map(self)
485     SV *self
486     PREINIT:
487         HV *const obj        = (HV *)SvRV(self);
488         SV *const class_name = HeVAL( hv_fetch_ent(obj, key_package, 0, hash_package) );
489         HV *const stash      = gv_stashsv(class_name, 0);
490         UV  const current    = check_package_cache_flag(stash);
491         SV *const cache_flag = HeVAL( hv_fetch_ent(obj, key_package_cache_flag, TRUE, hash_package_cache_flag));
492         SV *const map_ref    = HeVAL( hv_fetch_ent(obj, key_methods, TRUE, hash_methods));
493     PPCODE:
494         /* in  $self->{methods} does not yet exist (or got deleted) */
495         if ( !SvROK(map_ref) || SvTYPE(SvRV(map_ref)) != SVt_PVHV ) {
496             SV *new_map_ref = newRV_noinc((SV *)newHV());
497             sv_2mortal(new_map_ref);
498             sv_setsv(map_ref, new_map_ref);
499         }
500
501         if ( !SvOK(cache_flag) || SvUV(cache_flag) != current ) {
502             mop_update_method_map(aTHX_ self, class_name, stash, (HV *)SvRV(map_ref));
503             sv_setuv(cache_flag, check_package_cache_flag(stash)); /* update_cache_flag() */
504         }
505
506         XPUSHs(map_ref);