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