Implement get_all_attributes in XS
[gitmo/Mouse.git] / xs-src / Mouse.xs
1 #define  NEED_newSVpvn_flags_GLOBAL
2 #include "mouse.h"
3
4 SV* mouse_package;
5 SV* mouse_namespace;
6 SV* mouse_methods;
7 SV* mouse_name;
8
9 static SV* mouse_all_attrs_cache;
10 static SV* mouse_all_attrs_cache_gen;
11
12 AV*
13 mouse_get_all_attributes(pTHX_ SV* const metaclass){
14     SV* const package = get_slot(metaclass, mouse_package);
15     HV* const stash   = gv_stashsv(package, TRUE);
16     UV const pkg_gen  = mro_get_pkg_gen(stash);
17     SV* cache_gen     = get_slot(metaclass, mouse_all_attrs_cache_gen);
18
19     if(!(cache_gen && pkg_gen == SvUV(cache_gen))){ /* update */
20         CV* const get_metaclass  = get_cvs("Mouse::Util::get_metaclass_by_name", TRUE);
21         AV* const linearized_isa = mro_get_linear_isa(stash);
22         I32 const len            = AvFILLp(linearized_isa);
23         I32 i;
24         HV* seen;
25         AV* const all_attrs = newAV();
26
27         /* warn("Update all_attrs_cache (cache_gen %d != pkg_gen %d)", (cache_gen ? (int)SvIV(cache_gen) : 0), (int)pkg_gen); //*/
28
29         ENTER;
30         SAVETMPS;
31
32         set_slot(metaclass, mouse_all_attrs_cache, sv_2mortal(newRV_inc((SV*)all_attrs)));
33
34         seen = newHV();
35         sv_2mortal((SV*)seen);
36
37         for(i = 0; i < len; i++){
38             SV* const klass = MOUSE_av_at(linearized_isa, i);
39             SV* meta;
40             I32 n;
41             dSP;
42
43             PUSHMARK(SP);
44             XPUSHs(klass);
45             PUTBACK;
46
47             call_sv((SV*)get_metaclass, G_SCALAR);
48
49             SPAGAIN;
50             meta = POPs;
51             PUTBACK;
52
53             if(!SvOK(meta)){
54                 continue; /* skip non-Mouse classes */
55             }
56
57             /* $meta->get_attribute_list */
58             PUSHMARK(SP);
59             XPUSHs(meta);
60             PUTBACK;
61
62             n = call_method("get_attribute_list", G_ARRAY);
63             for(NOOP; n > 0; n--){
64                 SV* name;
65
66                 SPAGAIN;
67                 name = POPs;
68                 PUTBACK;
69
70                 if(hv_exists_ent(seen, name, 0U)){
71                     continue;
72                 }
73                 (void)hv_store_ent(seen, name, &PL_sv_undef, 0U);
74
75                 av_push(all_attrs, newSVsv( mcall1s(meta, "get_attribute", name) ));
76             }
77         }
78
79         if(!cache_gen){
80             cache_gen = sv_newmortal();
81         }
82         sv_setuv(cache_gen, mro_get_pkg_gen(stash));
83         set_slot(metaclass, mouse_all_attrs_cache_gen, cache_gen);
84
85         FREETMPS;
86         LEAVE;
87
88         return all_attrs;
89     }
90     else {
91         SV* const all_attrs_ref = get_slot(metaclass, mouse_all_attrs_cache);
92
93         if(!IsArrayRef(all_attrs_ref)){
94             croak("Not an ARRAY reference");
95         }
96
97         return (AV*)SvRV(all_attrs_ref);
98     }
99 }
100
101 MODULE = Mouse  PACKAGE = Mouse
102
103 PROTOTYPES: DISABLE
104
105 BOOT:
106     mouse_package   = newSVpvs_share("package");
107     mouse_namespace = newSVpvs_share("namespace");
108     mouse_methods   = newSVpvs_share("methods");
109     mouse_name      = newSVpvs_share("name");
110
111     mouse_all_attrs_cache      = newSVpvs_share("__all_attrs_cache");
112     mouse_all_attrs_cache_gen  = newSVpvs_share("__all_attrs_cache_gen");
113
114     MOUSE_CALL_BOOT(Mouse__Util);
115     MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints);
116     MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS);
117
118
119 MODULE = Mouse  PACKAGE = Mouse::Meta::Module
120
121 BOOT:
122     INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package);
123     INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods);
124     INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes);
125
126 HV*
127 namespace(SV* self)
128 CODE:
129 {
130     SV* const package = get_slot(self, mouse_package);
131     if(!(package && SvOK(package))){
132         croak("No package name defined");
133     }
134     RETVAL = gv_stashsv(package, GV_ADDMULTI);
135 }
136 OUTPUT:
137     RETVAL
138
139 # ignore extra arguments for extensibility
140 void
141 add_method(SV* self, SV* name, SV* code, ...)
142 CODE:
143 {
144     SV* const package = get_slot(self, mouse_package); /* $self->{package} */
145     SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */
146     GV* gv;
147     SV* code_ref;
148
149     if(!(package && SvOK(package))){
150         croak("No package name defined");
151     }
152
153     SvGETMAGIC(name);
154     SvGETMAGIC(code);
155
156     if(!SvOK(name)){
157         mouse_throw_error(self, NULL, "You must define a method name");
158     }
159     if(!SvROK(code)){
160         mouse_throw_error(self, NULL, "You must define a CODE reference");
161     }
162
163     code_ref = code;
164     if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){
165         SV*  sv = code_ref;  /* used in tryAMAGICunDEREF */
166         SV** sp = &sv;       /* used in tryAMAGICunDEREF */
167         tryAMAGICunDEREF(to_cv); /* try \&{$code} */
168         if(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV){
169             mouse_throw_error(self, NULL, "Not a CODE reference");
170         }
171         code_ref = sv;
172     }
173
174     /*  *{$package . '::' . $name} -> *gv */
175     gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV);
176     if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */
177         SvREFCNT_dec(GvCV(gv));
178         GvCV(gv) = NULL;
179     }
180     sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */
181
182     set_slot(methods, name, code); /* $self->{methods}{$name} = $code */
183
184     /* TODO: name the CODE ref if it's anonymous */
185     //code_entity = (CV*)SvRV(code_ref);
186     //if(CvANON(code_entity)
187     //    && CvGV(code_entity) /* a cv under construction has no gv */ ){
188
189     //    CvGV(code_entity) = gv;
190     //    CvANON_off(code_entity);
191     //}
192 }
193
194 MODULE = Mouse  PACKAGE = Mouse::Meta::Class
195
196 BOOT:
197     INSTALL_SIMPLE_READER(Class, roles);
198     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
199
200 void
201 linearized_isa(SV* self)
202 PPCODE:
203 {
204     SV* const stash_ref = mcall0(self, mouse_namespace); /* $self->namespace */
205     AV* linearized_isa;
206     I32 len;
207     I32 i;
208     if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)){
209         croak("namespace() didn't return a HASH reference");
210     }
211     linearized_isa = mro_get_linear_isa((HV*)SvRV(stash_ref));
212     len = AvFILLp(linearized_isa) + 1;
213     EXTEND(SP, len);
214     for(i = 0; i < len; i++){
215         PUSHs(AvARRAY(linearized_isa)[i]);
216     }
217 }
218
219 void
220 get_all_attributes(SV* self)
221 PPCODE:
222 {
223     AV* const all_attrs = mouse_get_all_attributes(aTHX_ self);
224     I32 const len       = AvFILLp(all_attrs) + 1;
225     I32 i;
226
227     EXTEND(SP, len);
228     for(i = 0; i < len; i++){
229         PUSHs( MOUSE_av_at(all_attrs, i) );
230     }
231 }
232
233 MODULE = Mouse  PACKAGE = Mouse::Meta::Role
234
235 BOOT:
236     INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles);
237     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id);
238
239 MODULE = Mouse  PACKAGE = Mouse::Meta::Attribute
240
241 BOOT:
242     /* readers */
243     INSTALL_SIMPLE_READER(Attribute, name);
244     INSTALL_SIMPLE_READER(Attribute, associated_class);
245     INSTALL_SIMPLE_READER(Attribute, accessor);
246     INSTALL_SIMPLE_READER(Attribute, reader);
247     INSTALL_SIMPLE_READER(Attribute, writer);
248     INSTALL_SIMPLE_READER(Attribute, predicate);
249     INSTALL_SIMPLE_READER(Attribute, clearer);
250     INSTALL_SIMPLE_READER(Attribute, handles);
251
252     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, _is_metadata, is);
253     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_required, required);
254     INSTALL_SIMPLE_READER(Attribute, default);
255     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_lazy, lazy);
256     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_lazy_build, lazy_build);
257     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_weak_ref, weak_ref);
258     INSTALL_SIMPLE_READER(Attribute, init_arg);
259     INSTALL_SIMPLE_READER(Attribute, type_constraint);
260     INSTALL_SIMPLE_READER(Attribute, trigger);
261     INSTALL_SIMPLE_READER(Attribute, builder);
262     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, should_auto_deref, auto_deref);
263     INSTALL_SIMPLE_READER_WITH_KEY(Attribute, should_coerce, coerce);
264     INSTALL_SIMPLE_READER(Attribute, documentation);
265
266     /* predicates */
267     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_accessor, accessor);
268     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_reader, reader);
269     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_writer, writer);
270     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_predicate, predicate);
271     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_clearer, clearer);
272     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_handles, handles);
273
274     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_default, default);
275     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_type_constraint, type_constraint);
276     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_trigger, trigger);
277     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_builder, builder);
278     INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_documentation, documentation);
279
280     newCONSTSUB(gv_stashpvs("Mouse::Meta::Attribute", TRUE), "accessor_metaclass",
281         newSVpvs("Mouse::Meta::Method::Accessor::XS"));
282