Add BUILDARGS tests
[gitmo/Mouse.git] / xs-src / Mouse.xs
CommitLineData
1b812057 1#define NEED_newSVpvn_flags_GLOBAL
df6dd016 2#include "mouse.h"
3
cccb83de 4SV* mouse_package;
5SV* mouse_namespace;
3e44140b 6SV* mouse_methods;
a5df48e5 7SV* mouse_name;
047d7af0 8SV* mouse_get_attribute;
9SV* mouse_get_attribute_list;
cccb83de 10
da4432f3 11
a39e9541 12#define MOUSE_xc_gen(a) MOUSE_av_at((a), MOUSE_XC_GEN)
13#define MOUSE_xc_attrall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) )
14#define MOUSE_xc_buildall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_BUILDALL) )
15#define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEOLISHALL) )
16
17/* Mouse XS Metaclass object */
18enum mouse_xc_ix_t{
19 MOUSE_XC_GEN, /* class generation */
20 MOUSE_XC_ATTRALL, /* all the attributes */
21 MOUSE_XC_BUILDALL, /* all the BUILD methods */
22 MOUSE_XC_DEMOLISHALL, /* all the DEMOLISH methods */
23
24 MOUSE_XC_last
25};
26
27static MGVTBL mouse_xc_vtbl; /* for identity */
28
29static void
30mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV* const seen){
31 dSP;
32 I32 n;
33
34 /* $meta->get_attribute_list */
35 PUSHMARK(SP);
36 XPUSHs(metaclass);
37 PUTBACK;
38
047d7af0 39 n = call_sv(mouse_get_attribute_list, G_ARRAY | G_METHOD);
a39e9541 40 for(NOOP; n > 0; n--){
41 SV* name;
42
43 SPAGAIN;
44 name = POPs;
45 PUTBACK;
46
47 if(hv_exists_ent(seen, name, 0U)){
48 continue;
49 }
50 (void)hv_store_ent(seen, name, &PL_sv_undef, 0U);
da4432f3 51
047d7af0 52 av_push(attrall, newSVsv( mcall1(metaclass, mouse_get_attribute, name) ));
a39e9541 53 }
54}
743ca82e 55
a39e9541 56static void
57mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stash, AV* const xc) {
58 AV* const linearized_isa = mro_get_linear_isa(stash);
59 I32 const len = AvFILLp(linearized_isa);
60 I32 i;
61 AV* const attrall = newAV();
62 AV* const buildall = newAV();
63 AV* const demolishall = newAV();
64 HV* const seen = newHV(); /* for attributes */
da4432f3 65
a39e9541 66 ENTER;
67 SAVETMPS;
da4432f3 68
a39e9541 69 sv_2mortal((SV*)seen);
da4432f3 70
a39e9541 71 /* old data will be delete at the end of the perl scope */
72 av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00);
73 av_delete(xc, MOUSE_XC_BUILDALL, 0x00);
74 av_delete(xc, MOUSE_XC_ATTRALL, 0x00);
743ca82e 75
a39e9541 76 SvREFCNT_inc_simple_void_NN(linearized_isa);
77 sv_2mortal((SV*)linearized_isa);
da4432f3 78
a39e9541 79 /* update */
da4432f3 80
a39e9541 81 av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall);
82 av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall);
83 av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall);
da4432f3 84
a39e9541 85 for(i = 0; i < len; i++){
047d7af0 86 SV* const klass = MOUSE_av_at(linearized_isa, i);
a39e9541 87 SV* meta;
88 GV* gv;
da4432f3 89
a39e9541 90 gv = stash_fetchs(stash, "BUILD", FALSE);
91 if(gv && GvCVu(gv)){
92 av_push(buildall, newRV_inc((SV*)GvCV(gv)));
93 }
da4432f3 94
a39e9541 95 gv = stash_fetchs(stash, "DEMOLISH", FALSE);
96 if(gv && GvCVu(gv)){
97 av_push(demolishall, newRV_inc((SV*)GvCV(gv)));
98 }
da4432f3 99
a39e9541 100 /* ATTRIBUTES */
101 meta = get_metaclass_by_name(klass);
102 if(!SvOK(meta)){
103 continue; /* skip non-Mouse classes */
104 }
da4432f3 105
a39e9541 106 mouse_class_push_attribute_list(aTHX_ meta, attrall, seen);
107 }
da4432f3 108
a39e9541 109 FREETMPS;
110 LEAVE;
da4432f3 111
a39e9541 112 sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash));
113}
da4432f3 114
a39e9541 115AV*
116mouse_get_xc(pTHX_ SV* const metaclass) {
117 AV* xc;
118 SV* gen;
119 HV* stash;
120 MAGIC* mg;
121
122 if(!IsObject(metaclass)){
123 croak("Not a Mouse metaclass");
124 }
da4432f3 125
a39e9541 126 mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00);
127 if(!mg){
128 SV* const package = get_slot(metaclass, mouse_package);
da4432f3 129
a39e9541 130 stash = gv_stashsv(package, TRUE);
131 xc = newAV();
da4432f3 132
a39e9541 133 mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext, &mouse_xc_vtbl, (char*)stash, HEf_SVKEY);
134 SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */
da4432f3 135
a39e9541 136 av_extend(xc, MOUSE_XC_last - 1);
137 av_store(xc, MOUSE_XC_GEN, newSViv(0));
da4432f3 138 }
a39e9541 139 else{
140 stash = (HV*)MOUSE_mg_ptr(mg);
141 xc = (AV*)MOUSE_mg_obj(mg);
da4432f3 142
a39e9541 143 assert(stash);
144 assert(SvTYPE(stash) == SVt_PVAV);
145
146 assert(xc);
147 assert(SvTYPE(xc) == SVt_PVAV);
148 }
da4432f3 149
a39e9541 150 gen = MOUSE_xc_gen(xc);
151 if(SvUV(gen) != mro_get_pkg_gen(stash)){
152 mouse_class_update_xc(aTHX_ metaclass, stash, xc);
da4432f3 153 }
a39e9541 154
155 return xc;
156}
157
158AV*
159mouse_get_all_attributes(pTHX_ SV* const metaclass) {
160 AV* const xc = mouse_get_xc(aTHX_ metaclass);
161 return MOUSE_xc_attrall(xc);
da4432f3 162}
163
646c0371 164MODULE = Mouse PACKAGE = Mouse
df6dd016 165
166PROTOTYPES: DISABLE
167
cccb83de 168BOOT:
169 mouse_package = newSVpvs_share("package");
170 mouse_namespace = newSVpvs_share("namespace");
3e44140b 171 mouse_methods = newSVpvs_share("methods");
a5df48e5 172 mouse_name = newSVpvs_share("name");
3e44140b 173
047d7af0 174 mouse_get_attribute = newSVpvs_share("get_attribute");
175 mouse_get_attribute_list = newSVpvs_share("get_attribute_list");
da4432f3 176
646c0371 177 MOUSE_CALL_BOOT(Mouse__Util);
1d5ecd5f 178 MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints);
646c0371 179 MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS);
f48920c1 180
cccb83de 181
7d96ae4d 182MODULE = Mouse PACKAGE = Mouse::Meta::Module
183
184BOOT:
185 INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package);
186 INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods);
187 INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes);
188
189HV*
190namespace(SV* self)
191CODE:
192{
6a97bbda 193 SV* const package = get_slot(self, mouse_package);
7d96ae4d 194 if(!(package && SvOK(package))){
3e44140b 195 croak("No package name defined");
7d96ae4d 196 }
197 RETVAL = gv_stashsv(package, GV_ADDMULTI);
198}
199OUTPUT:
200 RETVAL
201
3e44140b 202# ignore extra arguments for extensibility
203void
204add_method(SV* self, SV* name, SV* code, ...)
205CODE:
206{
6a97bbda 207 SV* const package = get_slot(self, mouse_package); /* $self->{package} */
208 SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */
3e44140b 209 GV* gv;
210 SV* code_ref;
211
212 if(!(package && SvOK(package))){
213 croak("No package name defined");
214 }
215
216 SvGETMAGIC(name);
217 SvGETMAGIC(code);
218
219 if(!SvOK(name)){
220 mouse_throw_error(self, NULL, "You must define a method name");
221 }
222 if(!SvROK(code)){
223 mouse_throw_error(self, NULL, "You must define a CODE reference");
224 }
225
226 code_ref = code;
227 if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){
228 SV* sv = code_ref; /* used in tryAMAGICunDEREF */
229 SV** sp = &sv; /* used in tryAMAGICunDEREF */
230 tryAMAGICunDEREF(to_cv); /* try \&{$code} */
231 if(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV){
232 mouse_throw_error(self, NULL, "Not a CODE reference");
233 }
234 code_ref = sv;
235 }
236
237 /* *{$package . '::' . $name} -> *gv */
238 gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV);
239 if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */
240 SvREFCNT_dec(GvCV(gv));
241 GvCV(gv) = NULL;
242 }
243 sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */
244
6fe2272b 245 set_slot(methods, name, code); /* $self->{methods}{$name} = $code */
3e44140b 246
247 /* TODO: name the CODE ref if it's anonymous */
248 //code_entity = (CV*)SvRV(code_ref);
249 //if(CvANON(code_entity)
250 // && CvGV(code_entity) /* a cv under construction has no gv */ ){
251
252 // CvGV(code_entity) = gv;
253 // CvANON_off(code_entity);
254 //}
255}
256
43165725 257MODULE = Mouse PACKAGE = Mouse::Meta::Class
258
259BOOT:
260 INSTALL_SIMPLE_READER(Class, roles);
261 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id);
262
cccb83de 263void
264linearized_isa(SV* self)
265PPCODE:
266{
267 SV* const stash_ref = mcall0(self, mouse_namespace); /* $self->namespace */
268 AV* linearized_isa;
269 I32 len;
270 I32 i;
271 if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)){
272 croak("namespace() didn't return a HASH reference");
273 }
274 linearized_isa = mro_get_linear_isa((HV*)SvRV(stash_ref));
275 len = AvFILLp(linearized_isa) + 1;
276 EXTEND(SP, len);
277 for(i = 0; i < len; i++){
278 PUSHs(AvARRAY(linearized_isa)[i]);
279 }
280}
281
da4432f3 282void
283get_all_attributes(SV* self)
284PPCODE:
285{
286 AV* const all_attrs = mouse_get_all_attributes(aTHX_ self);
287 I32 const len = AvFILLp(all_attrs) + 1;
288 I32 i;
289
290 EXTEND(SP, len);
291 for(i = 0; i < len; i++){
292 PUSHs( MOUSE_av_at(all_attrs, i) );
293 }
294}
441964ce 295
43165725 296MODULE = Mouse PACKAGE = Mouse::Meta::Role
297
298BOOT:
299 INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles);
300 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id);
301
43165725 302MODULE = Mouse PACKAGE = Mouse::Meta::Attribute
303
304BOOT:
305 /* readers */
306 INSTALL_SIMPLE_READER(Attribute, name);
307 INSTALL_SIMPLE_READER(Attribute, associated_class);
308 INSTALL_SIMPLE_READER(Attribute, accessor);
309 INSTALL_SIMPLE_READER(Attribute, reader);
310 INSTALL_SIMPLE_READER(Attribute, writer);
311 INSTALL_SIMPLE_READER(Attribute, predicate);
312 INSTALL_SIMPLE_READER(Attribute, clearer);
313 INSTALL_SIMPLE_READER(Attribute, handles);
314
315 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, _is_metadata, is);
316 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_required, required);
317 INSTALL_SIMPLE_READER(Attribute, default);
318 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_lazy, lazy);
319 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_lazy_build, lazy_build);
320 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, is_weak_ref, weak_ref);
321 INSTALL_SIMPLE_READER(Attribute, init_arg);
322 INSTALL_SIMPLE_READER(Attribute, type_constraint);
323 INSTALL_SIMPLE_READER(Attribute, trigger);
324 INSTALL_SIMPLE_READER(Attribute, builder);
325 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, should_auto_deref, auto_deref);
326 INSTALL_SIMPLE_READER_WITH_KEY(Attribute, should_coerce, coerce);
327 INSTALL_SIMPLE_READER(Attribute, documentation);
328
329 /* predicates */
330 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_accessor, accessor);
331 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_reader, reader);
332 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_writer, writer);
333 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_predicate, predicate);
334 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_clearer, clearer);
335 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_handles, handles);
336
337 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_default, default);
338 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_type_constraint, type_constraint);
339 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_trigger, trigger);
340 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_builder, builder);
341 INSTALL_SIMPLE_PREDICATE_WITH_KEY(Attribute, has_documentation, documentation);
342
93540011 343 newCONSTSUB(gv_stashpvs("Mouse::Meta::Attribute", TRUE), "accessor_metaclass",
344 newSVpvs("Mouse::Meta::Method::Accessor::XS"));
345