test fail with Sub::Name 0.04; bump prereq
[gitmo/Class-MOP.git] / xs / HasMethods.xs
CommitLineData
9b871d79 1#include "mop.h"
2
3SV *mop_method_metaclass;
4SV *mop_associated_metaclass;
5SV *mop_wrap;
6
7static void
8mop_update_method_map(pTHX_ SV *const self, SV *const class_name, HV *const stash, HV *const map)
9{
10 const char *const class_name_pv = HvNAME(stash); /* must be HvNAME(stash), not SvPV_nolen_const(class_name) */
11 SV *method_metaclass_name;
12 char *method_name;
13 I32 method_name_len;
14 SV *coderef;
15 HV *symbols;
16 dSP;
17
18 symbols = mop_get_all_package_symbols(stash, TYPE_FILTER_CODE);
19 sv_2mortal((SV*)symbols);
20 (void)hv_iterinit(symbols);
21 while ( (coderef = hv_iternextsv(symbols, &method_name, &method_name_len)) ) {
22 CV *cv = (CV *)SvRV(coderef);
23 char *cvpkg_name;
24 char *cv_name;
25 SV *method_slot;
26 SV *method_object;
27
28 if (!mop_get_code_info(coderef, &cvpkg_name, &cv_name)) {
29 continue;
30 }
31
32 /* this checks to see that the subroutine is actually from our package */
33 if ( !(strEQ(cvpkg_name, "constant") && strEQ(cv_name, "__ANON__")) ) {
34 if ( strNE(cvpkg_name, class_name_pv) ) {
35 continue;
36 }
37 }
38
39 method_slot = *hv_fetch(map, method_name, method_name_len, TRUE);
40 if ( SvOK(method_slot) ) {
41 SV *body;
42
43 if ( sv_isobject(method_slot) ) {
44 body = mop_call0(aTHX_ method_slot, KEY_FOR(body)); /* $method_object->body() */
45 }
46 else {
47 body = method_slot;
48 }
49
50 if ( SvROK(body) && ((CV *) SvRV(body)) == cv ) {
51 continue;
52 }
53 }
54
55 method_metaclass_name = mop_call0(aTHX_ self, mop_method_metaclass); /* $self->method_metaclass() */
56
57 /*
58 $method_object = $method_metaclass->wrap(
59 $cv,
60 associated_metaclass => $self,
61 package_name => $class_name,
62 name => $method_name
63 );
64 */
65 ENTER;
66 SAVETMPS;
67
68 PUSHMARK(SP);
69 EXTEND(SP, 8);
70 PUSHs(method_metaclass_name); /* invocant */
71 mPUSHs(newRV_inc((SV *)cv));
72 PUSHs(mop_associated_metaclass);
73 PUSHs(self);
74 PUSHs(KEY_FOR(package_name));
75 PUSHs(class_name);
76 PUSHs(KEY_FOR(name));
77 mPUSHs(newSVpv(method_name, method_name_len));
78 PUTBACK;
79
80 call_sv(mop_wrap, G_SCALAR | G_METHOD);
81 SPAGAIN;
82 method_object = POPs;
83 PUTBACK;
84 /* $map->{$method_name} = $method_object */
85 sv_setsv(method_slot, method_object);
86
87 FREETMPS;
88 LEAVE;
89 }
90}
91
92MODULE = Class::MOP::Mixin::HasMethods PACKAGE = Class::MOP::Mixin::HasMethods
93
94PROTOTYPES: DISABLE
95
96void
97_full_method_map(self)
98 SV *self
99 PREINIT:
100 HV *const obj = (HV *)SvRV(self);
101 SV *const class_name = HeVAL( hv_fetch_ent(obj, KEY_FOR(package), 0, HASH_FOR(package)) );
102 HV *const stash = gv_stashsv(class_name, 0);
103 UV current;
104 SV *cache_flag;
105 SV *map_ref;
106 PPCODE:
107 if (!stash) {
108 mXPUSHs(newRV_noinc((SV *)newHV()));
109 return;
110 }
111
112 current = mop_check_package_cache_flag(aTHX_ stash);
113 cache_flag = HeVAL( hv_fetch_ent(obj, KEY_FOR(package_cache_flag), TRUE, HASH_FOR(package_cache_flag)));
114 map_ref = HeVAL( hv_fetch_ent(obj, KEY_FOR(methods), TRUE, HASH_FOR(methods)));
115
116 /* $self->{methods} does not yet exist (or got deleted) */
117 if ( !SvROK(map_ref) || SvTYPE(SvRV(map_ref)) != SVt_PVHV ) {
118 SV *new_map_ref = newRV_noinc((SV *)newHV());
119 sv_2mortal(new_map_ref);
120 sv_setsv(map_ref, new_map_ref);
121 }
122
123 if ( !SvOK(cache_flag) || SvUV(cache_flag) != current ) {
124 mop_update_method_map(aTHX_ self, class_name, stash, (HV *)SvRV(map_ref));
125 sv_setuv(cache_flag, mop_check_package_cache_flag(aTHX_ stash)); /* update_cache_flag() */
126 }
127
128 XPUSHs(map_ref);
129
130BOOT:
131 mop_method_metaclass = newSVpvs("method_metaclass");
132 mop_associated_metaclass = newSVpvs("associated_metaclass");
133 mop_wrap = newSVpvs("wrap");