Commit | Line | Data |
1b812057 |
1 | #define NEED_newSVpvn_flags_GLOBAL |
df6dd016 |
2 | #include "mouse.h" |
3 | |
cccb83de |
4 | SV* mouse_package; |
5 | SV* mouse_namespace; |
3e44140b |
6 | SV* mouse_methods; |
a5df48e5 |
7 | SV* mouse_name; |
047d7af0 |
8 | SV* mouse_get_attribute; |
9 | SV* mouse_get_attribute_list; |
cccb83de |
10 | |
da4432f3 |
11 | |
a39e9541 |
12 | #define MOUSE_xc_gen(a) MOUSE_av_at((a), MOUSE_XC_GEN) |
aa2d2e2c |
13 | #define MOUSE_xc_stash(a) ( (HV*)MOUSE_av_at((a), MOUSE_XC_STASH) ) |
a39e9541 |
14 | #define MOUSE_xc_attrall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) ) |
15 | #define MOUSE_xc_buildall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_BUILDALL) ) |
16 | #define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEOLISHALL) ) |
17 | |
aa2d2e2c |
18 | enum mouse_xc_flags_t { |
19 | MOUSEf_XC_IS_IMMUTABLE = 0x0001, |
20 | MOUSEf_XC_IS_ANON = 0x0002, |
21 | MOUSEf_XC_HAS_BUILDARGS = 0x0004, |
22 | |
23 | MOUSEf_XC_mask = 0xFFFF /* not used */ |
24 | }; |
25 | |
a39e9541 |
26 | /* Mouse XS Metaclass object */ |
27 | enum mouse_xc_ix_t{ |
28 | MOUSE_XC_GEN, /* class generation */ |
aa2d2e2c |
29 | MOUSE_XC_STASH, /* symbol table hash */ |
30 | MOUSE_XC_FLAGS, |
31 | |
32 | MOUSE_XC_BUILDARGS, /* Custom BUILDARGS */ |
33 | |
a39e9541 |
34 | MOUSE_XC_ATTRALL, /* all the attributes */ |
35 | MOUSE_XC_BUILDALL, /* all the BUILD methods */ |
36 | MOUSE_XC_DEMOLISHALL, /* all the DEMOLISH methods */ |
37 | |
38 | MOUSE_XC_last |
39 | }; |
40 | |
41 | static MGVTBL mouse_xc_vtbl; /* for identity */ |
42 | |
43 | static void |
44 | mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV* const seen){ |
45 | dSP; |
46 | I32 n; |
47 | |
48 | /* $meta->get_attribute_list */ |
49 | PUSHMARK(SP); |
50 | XPUSHs(metaclass); |
51 | PUTBACK; |
52 | |
047d7af0 |
53 | n = call_sv(mouse_get_attribute_list, G_ARRAY | G_METHOD); |
a39e9541 |
54 | for(NOOP; n > 0; n--){ |
55 | SV* name; |
56 | |
57 | SPAGAIN; |
58 | name = POPs; |
59 | PUTBACK; |
60 | |
61 | if(hv_exists_ent(seen, name, 0U)){ |
62 | continue; |
63 | } |
64 | (void)hv_store_ent(seen, name, &PL_sv_undef, 0U); |
da4432f3 |
65 | |
047d7af0 |
66 | av_push(attrall, newSVsv( mcall1(metaclass, mouse_get_attribute, name) )); |
a39e9541 |
67 | } |
68 | } |
743ca82e |
69 | |
aa2d2e2c |
70 | static int |
71 | mouse_class_has_custom_buildargs(pTHX_ HV* const stash) { |
72 | XS(XS_Mouse__Object_BUILDARGS); /* prototype */ |
73 | |
74 | GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0); |
75 | CV* const cv = GvCV(buildargs); |
76 | |
77 | assert(cv); |
78 | return CvXSUB(cv) == XS_Mouse__Object_BUILDARGS; |
79 | } |
80 | |
a39e9541 |
81 | static void |
82 | mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stash, AV* const xc) { |
83 | AV* const linearized_isa = mro_get_linear_isa(stash); |
84 | I32 const len = AvFILLp(linearized_isa); |
85 | I32 i; |
aa2d2e2c |
86 | U32 flags = 0x00; |
a39e9541 |
87 | AV* const attrall = newAV(); |
88 | AV* const buildall = newAV(); |
89 | AV* const demolishall = newAV(); |
90 | HV* const seen = newHV(); /* for attributes */ |
da4432f3 |
91 | |
a39e9541 |
92 | ENTER; |
93 | SAVETMPS; |
da4432f3 |
94 | |
a39e9541 |
95 | sv_2mortal((SV*)seen); |
da4432f3 |
96 | |
a39e9541 |
97 | /* old data will be delete at the end of the perl scope */ |
98 | av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00); |
99 | av_delete(xc, MOUSE_XC_BUILDALL, 0x00); |
100 | av_delete(xc, MOUSE_XC_ATTRALL, 0x00); |
743ca82e |
101 | |
a39e9541 |
102 | SvREFCNT_inc_simple_void_NN(linearized_isa); |
103 | sv_2mortal((SV*)linearized_isa); |
da4432f3 |
104 | |
a39e9541 |
105 | /* update */ |
da4432f3 |
106 | |
aa2d2e2c |
107 | if(SvTRUEx( mcall0s(metaclass, "is_immutable") )){ |
108 | flags |= MOUSEf_XC_IS_IMMUTABLE; |
109 | } |
110 | |
111 | if(mouse_class_has_custom_buildargs(aTHX_ stash)){ |
112 | flags |= MOUSEf_XC_HAS_BUILDARGS; |
113 | } |
114 | |
a39e9541 |
115 | av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall); |
116 | av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall); |
117 | av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall); |
da4432f3 |
118 | |
a39e9541 |
119 | for(i = 0; i < len; i++){ |
047d7af0 |
120 | SV* const klass = MOUSE_av_at(linearized_isa, i); |
a39e9541 |
121 | SV* meta; |
122 | GV* gv; |
da4432f3 |
123 | |
a39e9541 |
124 | gv = stash_fetchs(stash, "BUILD", FALSE); |
125 | if(gv && GvCVu(gv)){ |
126 | av_push(buildall, newRV_inc((SV*)GvCV(gv))); |
127 | } |
da4432f3 |
128 | |
a39e9541 |
129 | gv = stash_fetchs(stash, "DEMOLISH", FALSE); |
130 | if(gv && GvCVu(gv)){ |
131 | av_push(demolishall, newRV_inc((SV*)GvCV(gv))); |
132 | } |
da4432f3 |
133 | |
a39e9541 |
134 | /* ATTRIBUTES */ |
aa2d2e2c |
135 | meta = get_metaclass(klass); |
a39e9541 |
136 | if(!SvOK(meta)){ |
137 | continue; /* skip non-Mouse classes */ |
138 | } |
da4432f3 |
139 | |
a39e9541 |
140 | mouse_class_push_attribute_list(aTHX_ meta, attrall, seen); |
141 | } |
da4432f3 |
142 | |
a39e9541 |
143 | FREETMPS; |
144 | LEAVE; |
da4432f3 |
145 | |
a39e9541 |
146 | sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash)); |
147 | } |
da4432f3 |
148 | |
a39e9541 |
149 | AV* |
150 | mouse_get_xc(pTHX_ SV* const metaclass) { |
151 | AV* xc; |
152 | SV* gen; |
153 | HV* stash; |
154 | MAGIC* mg; |
155 | |
156 | if(!IsObject(metaclass)){ |
157 | croak("Not a Mouse metaclass"); |
158 | } |
da4432f3 |
159 | |
a39e9541 |
160 | mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00); |
161 | if(!mg){ |
162 | SV* const package = get_slot(metaclass, mouse_package); |
aa2d2e2c |
163 | STRLEN len; |
164 | const char* const pv = SvPV_const(package, len); |
da4432f3 |
165 | |
aa2d2e2c |
166 | stash = gv_stashpvn(pv, len, TRUE); |
a39e9541 |
167 | xc = newAV(); |
da4432f3 |
168 | |
aa2d2e2c |
169 | mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext, &mouse_xc_vtbl, pv, len); |
a39e9541 |
170 | SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */ |
da4432f3 |
171 | |
a39e9541 |
172 | av_extend(xc, MOUSE_XC_last - 1); |
aa2d2e2c |
173 | |
a39e9541 |
174 | av_store(xc, MOUSE_XC_GEN, newSViv(0)); |
aa2d2e2c |
175 | av_store(xc, MOUSE_XC_STASH, (SV*)stash); |
176 | SvREFCNT_inc_simple_void_NN(stash); |
da4432f3 |
177 | } |
a39e9541 |
178 | else{ |
a39e9541 |
179 | xc = (AV*)MOUSE_mg_obj(mg); |
da4432f3 |
180 | |
a39e9541 |
181 | assert(xc); |
182 | assert(SvTYPE(xc) == SVt_PVAV); |
183 | } |
da4432f3 |
184 | |
aa2d2e2c |
185 | gen = MOUSE_xc_gen(xc); |
186 | stash = MOUSE_xc_stash(xc); |
187 | |
a39e9541 |
188 | if(SvUV(gen) != mro_get_pkg_gen(stash)){ |
189 | mouse_class_update_xc(aTHX_ metaclass, stash, xc); |
da4432f3 |
190 | } |
a39e9541 |
191 | |
192 | return xc; |
193 | } |
194 | |
195 | AV* |
196 | mouse_get_all_attributes(pTHX_ SV* const metaclass) { |
197 | AV* const xc = mouse_get_xc(aTHX_ metaclass); |
198 | return MOUSE_xc_attrall(xc); |
da4432f3 |
199 | } |
200 | |
aa2d2e2c |
201 | HV* |
202 | mouse_build_args(aTHX_ SV* metaclass, SV* const klass, I32 const start, I32 const items, I32 const ax) { |
203 | HV* args; |
204 | if((items - start) == 1){ |
205 | SV* const args_ref = ST(start); |
206 | if(!IsHashRef(args_ref)){ |
207 | if(!metaclass){ metaclass = get_metaclass(klass); } |
208 | mouse_throw_error(metaclass, NULL, "Single parameters to new() must be a HASH ref"); |
209 | } |
210 | args = newHVhv((HV*)SvRV(args_ref)); |
211 | sv_2mortal((SV*)args); |
212 | } |
213 | else{ |
214 | I32 i; |
215 | |
6fab29c6 |
216 | args = newHV_mortal(); |
aa2d2e2c |
217 | |
218 | if( ((items - start) % 2) != 0 ){ |
219 | if(!metaclass){ metaclass = get_metaclass(klass); } |
220 | mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()"); |
221 | } |
222 | |
223 | for(i = start; i < items; i += 2){ |
224 | (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U); |
225 | } |
226 | |
227 | } |
228 | return args; |
229 | } |
230 | |
231 | void |
d4712779 |
232 | mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) { |
233 | AV* const xc = mouse_get_xc(aTHX_ meta); |
234 | AV* const attrs = MOUSE_xc_attrall(xc); |
235 | I32 const len = AvFILLp(attrs) + 1; |
236 | I32 i; |
237 | AV* const triggers_queue = (invoke_triggers ? newAV_mortal() : NULL); |
238 | for(i = 0; i < len; i++){ |
239 | AV* const = mouse_get_xa(aTHX_ AvARRAY(attrs)[i]); |
240 | } |
aa2d2e2c |
241 | } |
242 | |
646c0371 |
243 | MODULE = Mouse PACKAGE = Mouse |
df6dd016 |
244 | |
245 | PROTOTYPES: DISABLE |
246 | |
cccb83de |
247 | BOOT: |
248 | mouse_package = newSVpvs_share("package"); |
249 | mouse_namespace = newSVpvs_share("namespace"); |
3e44140b |
250 | mouse_methods = newSVpvs_share("methods"); |
a5df48e5 |
251 | mouse_name = newSVpvs_share("name"); |
3e44140b |
252 | |
047d7af0 |
253 | mouse_get_attribute = newSVpvs_share("get_attribute"); |
254 | mouse_get_attribute_list = newSVpvs_share("get_attribute_list"); |
da4432f3 |
255 | |
646c0371 |
256 | MOUSE_CALL_BOOT(Mouse__Util); |
1d5ecd5f |
257 | MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints); |
646c0371 |
258 | MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS); |
aa2d2e2c |
259 | MOUSE_CALL_BOOT(Mouse__Meta__Attribute); |
f48920c1 |
260 | |
cccb83de |
261 | |
7d96ae4d |
262 | MODULE = Mouse PACKAGE = Mouse::Meta::Module |
263 | |
264 | BOOT: |
265 | INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package); |
266 | INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods); |
267 | INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes); |
268 | |
269 | HV* |
270 | namespace(SV* self) |
271 | CODE: |
272 | { |
6a97bbda |
273 | SV* const package = get_slot(self, mouse_package); |
7d96ae4d |
274 | if(!(package && SvOK(package))){ |
3e44140b |
275 | croak("No package name defined"); |
7d96ae4d |
276 | } |
277 | RETVAL = gv_stashsv(package, GV_ADDMULTI); |
278 | } |
279 | OUTPUT: |
280 | RETVAL |
281 | |
3e44140b |
282 | # ignore extra arguments for extensibility |
283 | void |
284 | add_method(SV* self, SV* name, SV* code, ...) |
285 | CODE: |
286 | { |
6a97bbda |
287 | SV* const package = get_slot(self, mouse_package); /* $self->{package} */ |
288 | SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */ |
3e44140b |
289 | GV* gv; |
290 | SV* code_ref; |
291 | |
292 | if(!(package && SvOK(package))){ |
293 | croak("No package name defined"); |
294 | } |
295 | |
296 | SvGETMAGIC(name); |
297 | SvGETMAGIC(code); |
298 | |
299 | if(!SvOK(name)){ |
300 | mouse_throw_error(self, NULL, "You must define a method name"); |
301 | } |
302 | if(!SvROK(code)){ |
303 | mouse_throw_error(self, NULL, "You must define a CODE reference"); |
304 | } |
305 | |
306 | code_ref = code; |
307 | if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){ |
308 | SV* sv = code_ref; /* used in tryAMAGICunDEREF */ |
309 | SV** sp = &sv; /* used in tryAMAGICunDEREF */ |
310 | tryAMAGICunDEREF(to_cv); /* try \&{$code} */ |
311 | if(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV){ |
312 | mouse_throw_error(self, NULL, "Not a CODE reference"); |
313 | } |
314 | code_ref = sv; |
315 | } |
316 | |
317 | /* *{$package . '::' . $name} -> *gv */ |
318 | gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV); |
319 | if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */ |
320 | SvREFCNT_dec(GvCV(gv)); |
321 | GvCV(gv) = NULL; |
322 | } |
323 | sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */ |
324 | |
6fe2272b |
325 | set_slot(methods, name, code); /* $self->{methods}{$name} = $code */ |
3e44140b |
326 | |
327 | /* TODO: name the CODE ref if it's anonymous */ |
328 | //code_entity = (CV*)SvRV(code_ref); |
329 | //if(CvANON(code_entity) |
330 | // && CvGV(code_entity) /* a cv under construction has no gv */ ){ |
331 | |
332 | // CvGV(code_entity) = gv; |
333 | // CvANON_off(code_entity); |
334 | //} |
335 | } |
336 | |
43165725 |
337 | MODULE = Mouse PACKAGE = Mouse::Meta::Class |
338 | |
339 | BOOT: |
340 | INSTALL_SIMPLE_READER(Class, roles); |
341 | INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id); |
342 | |
cccb83de |
343 | void |
344 | linearized_isa(SV* self) |
345 | PPCODE: |
346 | { |
347 | SV* const stash_ref = mcall0(self, mouse_namespace); /* $self->namespace */ |
348 | AV* linearized_isa; |
349 | I32 len; |
350 | I32 i; |
351 | if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)){ |
352 | croak("namespace() didn't return a HASH reference"); |
353 | } |
354 | linearized_isa = mro_get_linear_isa((HV*)SvRV(stash_ref)); |
355 | len = AvFILLp(linearized_isa) + 1; |
356 | EXTEND(SP, len); |
357 | for(i = 0; i < len; i++){ |
358 | PUSHs(AvARRAY(linearized_isa)[i]); |
359 | } |
360 | } |
361 | |
da4432f3 |
362 | void |
363 | get_all_attributes(SV* self) |
364 | PPCODE: |
365 | { |
366 | AV* const all_attrs = mouse_get_all_attributes(aTHX_ self); |
367 | I32 const len = AvFILLp(all_attrs) + 1; |
368 | I32 i; |
369 | |
370 | EXTEND(SP, len); |
371 | for(i = 0; i < len; i++){ |
372 | PUSHs( MOUSE_av_at(all_attrs, i) ); |
373 | } |
374 | } |
441964ce |
375 | |
aa2d2e2c |
376 | SV* |
377 | new_object_(SV* meta, ...) |
378 | CODE: |
379 | { |
380 | HV* const args = mouse_build_args(aTHX_ meta, NULL, 1, items, ax); |
381 | AV* const xc = mouse_get_xc(aTHX_ meta); |
d4712779 |
382 | |
aa2d2e2c |
383 | RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); |
d4712779 |
384 | mouse_class_initialize_object(aTHX_ meta, RETVAL, args, FALSE); |
aa2d2e2c |
385 | } |
386 | |
387 | |
388 | void |
d4712779 |
389 | _initialize_object_(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE) |
aa2d2e2c |
390 | CODE: |
391 | { |
d4712779 |
392 | mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers); |
aa2d2e2c |
393 | } |
394 | |
43165725 |
395 | MODULE = Mouse PACKAGE = Mouse::Meta::Role |
396 | |
397 | BOOT: |
398 | INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles); |
399 | INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id); |
400 | |
aa2d2e2c |
401 | MODULE = Mouse PACKAGE = Mouse::Object |
43165725 |
402 | |
aa2d2e2c |
403 | HV* |
404 | BUILDARGS(SV* klass, ...) |
405 | CODE: |
406 | { |
407 | RETVAL = mouse_build_args(aTHX_ NULL, klass, 1, items, ax); |
408 | } |
409 | OUTPUT: |
410 | RETVAL |
93540011 |
411 | |