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