Commit | Line | Data |
1b812057 |
1 | #define NEED_newSVpvn_flags_GLOBAL |
df6dd016 |
2 | #include "mouse.h" |
3 | |
d39ac61c |
4 | /* keywords for methods/keys */ |
cccb83de |
5 | SV* mouse_package; |
6 | SV* mouse_namespace; |
3e44140b |
7 | SV* mouse_methods; |
a5df48e5 |
8 | SV* mouse_name; |
047d7af0 |
9 | SV* mouse_get_attribute; |
10 | SV* mouse_get_attribute_list; |
d39ac61c |
11 | SV* mouse_coerce; |
cccb83de |
12 | |
4e7e3250 |
13 | #define MOUSE_xc_flags(a) SvUVX(MOUSE_av_at((a), MOUSE_XC_FLAGS)) |
a39e9541 |
14 | #define MOUSE_xc_gen(a) MOUSE_av_at((a), MOUSE_XC_GEN) |
aa2d2e2c |
15 | #define MOUSE_xc_stash(a) ( (HV*)MOUSE_av_at((a), MOUSE_XC_STASH) ) |
a39e9541 |
16 | #define MOUSE_xc_attrall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_ATTRALL) ) |
17 | #define MOUSE_xc_buildall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_BUILDALL) ) |
9974d611 |
18 | #define MOUSE_xc_demolishall(a) ( (AV*)MOUSE_av_at((a), MOUSE_XC_DEMOLISHALL) ) |
a39e9541 |
19 | |
aa2d2e2c |
20 | enum mouse_xc_flags_t { |
21 | MOUSEf_XC_IS_IMMUTABLE = 0x0001, |
22 | MOUSEf_XC_IS_ANON = 0x0002, |
23 | MOUSEf_XC_HAS_BUILDARGS = 0x0004, |
e128626c |
24 | MOUSEf_XC_CONSTRUCTOR_IS_STRICT |
25 | = 0x0008, |
aa2d2e2c |
26 | |
27 | MOUSEf_XC_mask = 0xFFFF /* not used */ |
28 | }; |
29 | |
a39e9541 |
30 | /* Mouse XS Metaclass object */ |
31 | enum mouse_xc_ix_t{ |
4e7e3250 |
32 | MOUSE_XC_FLAGS, |
33 | |
a39e9541 |
34 | MOUSE_XC_GEN, /* class generation */ |
aa2d2e2c |
35 | MOUSE_XC_STASH, /* symbol table hash */ |
aa2d2e2c |
36 | |
a39e9541 |
37 | MOUSE_XC_ATTRALL, /* all the attributes */ |
38 | MOUSE_XC_BUILDALL, /* all the BUILD methods */ |
39 | MOUSE_XC_DEMOLISHALL, /* all the DEMOLISH methods */ |
40 | |
41 | MOUSE_XC_last |
42 | }; |
43 | |
cb60d0b5 |
44 | enum mouse_modifier_t { |
45 | MOUSE_M_BEFORE, |
46 | MOUSE_M_AROUND, |
47 | MOUSE_M_AFTER, |
48 | }; |
49 | |
a39e9541 |
50 | static MGVTBL mouse_xc_vtbl; /* for identity */ |
51 | |
05e658dc |
52 | HV* |
53 | mouse_get_namespace(pTHX_ SV* const meta) { |
54 | SV* const package = get_slot(meta, mouse_package); |
55 | if(!(package && SvOK(package))){ |
56 | croak("No package name defined for metaclass"); |
57 | } |
58 | return gv_stashsv(package, GV_ADDMULTI); |
59 | } |
60 | |
abbcd124 |
61 | static AV* |
62 | mouse_calculate_all_attributes(pTHX_ SV* const metaclass) { |
63 | SV* const avref = mcall0s(metaclass, "_calculate_all_attributes"); |
64 | if(!(SvROK(avref) && SvTYPE(SvRV(avref)) == SVt_PVAV)) { |
65 | croak("$meta->_calculate_all_attributes did not return an ARRAY reference"); |
a39e9541 |
66 | } |
abbcd124 |
67 | return (AV*)SvRV(avref); |
a39e9541 |
68 | } |
743ca82e |
69 | |
fe5044b1 |
70 | XS(XS_Mouse__Object_BUILDARGS); /* prototype */ |
71 | |
aa2d2e2c |
72 | static int |
73 | mouse_class_has_custom_buildargs(pTHX_ HV* const stash) { |
aa2d2e2c |
74 | GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0); |
aa2d2e2c |
75 | |
12872cc1 |
76 | return buildargs && CvXSUB(GvCV(buildargs)) != XS_Mouse__Object_BUILDARGS; |
aa2d2e2c |
77 | } |
78 | |
bdf225bb |
79 | static AV* |
80 | mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, AV* const xc) { |
81 | HV* const stash = MOUSE_xc_stash(xc); |
a39e9541 |
82 | AV* const linearized_isa = mro_get_linear_isa(stash); |
abc8d595 |
83 | I32 const len = AvFILLp(linearized_isa) + 1; |
a39e9541 |
84 | I32 i; |
aa2d2e2c |
85 | U32 flags = 0x00; |
a39e9541 |
86 | AV* const buildall = newAV(); |
87 | AV* const demolishall = newAV(); |
abbcd124 |
88 | AV* attrall; |
da4432f3 |
89 | |
a39e9541 |
90 | ENTER; |
91 | SAVETMPS; |
da4432f3 |
92 | |
a39e9541 |
93 | /* old data will be delete at the end of the perl scope */ |
94 | av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00); |
95 | av_delete(xc, MOUSE_XC_BUILDALL, 0x00); |
96 | av_delete(xc, MOUSE_XC_ATTRALL, 0x00); |
743ca82e |
97 | |
a39e9541 |
98 | SvREFCNT_inc_simple_void_NN(linearized_isa); |
99 | sv_2mortal((SV*)linearized_isa); |
da4432f3 |
100 | |
a39e9541 |
101 | /* update */ |
da4432f3 |
102 | |
abbcd124 |
103 | av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall); |
104 | av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall); |
105 | |
106 | attrall = mouse_calculate_all_attributes(aTHX_ metaclass); |
107 | SvREFCNT_inc_simple_void_NN(attrall); |
108 | av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall); |
109 | |
4e7e3250 |
110 | if(predicate_calls(metaclass, "is_immutable")){ |
aa2d2e2c |
111 | flags |= MOUSEf_XC_IS_IMMUTABLE; |
112 | } |
113 | |
4e7e3250 |
114 | if(predicate_calls(metaclass, "is_anon_class")){ |
115 | flags |= MOUSEf_XC_IS_ANON; |
116 | } |
117 | |
aa2d2e2c |
118 | if(mouse_class_has_custom_buildargs(aTHX_ stash)){ |
119 | flags |= MOUSEf_XC_HAS_BUILDARGS; |
120 | } |
121 | |
fb4ddd88 |
122 | if(predicate_calls(metaclass, "strict_constructor")){ |
e128626c |
123 | flags |= MOUSEf_XC_CONSTRUCTOR_IS_STRICT; |
124 | } |
125 | |
4e7e3250 |
126 | av_store(xc, MOUSE_XC_FLAGS, newSVuv(flags)); |
da4432f3 |
127 | |
a39e9541 |
128 | for(i = 0; i < len; i++){ |
047d7af0 |
129 | SV* const klass = MOUSE_av_at(linearized_isa, i); |
074a414d |
130 | HV* const st = gv_stashsv(klass, TRUE); |
a39e9541 |
131 | GV* gv; |
da4432f3 |
132 | |
074a414d |
133 | gv = stash_fetchs(st, "BUILD", FALSE); |
a39e9541 |
134 | if(gv && GvCVu(gv)){ |
074a414d |
135 | av_unshift(buildall, 1); |
136 | av_store(buildall, 0, newRV_inc((SV*)GvCV(gv))); |
a39e9541 |
137 | } |
da4432f3 |
138 | |
074a414d |
139 | gv = stash_fetchs(st, "DEMOLISH", FALSE); |
a39e9541 |
140 | if(gv && GvCVu(gv)){ |
141 | av_push(demolishall, newRV_inc((SV*)GvCV(gv))); |
142 | } |
a39e9541 |
143 | } |
da4432f3 |
144 | |
a39e9541 |
145 | FREETMPS; |
146 | LEAVE; |
da4432f3 |
147 | |
a39e9541 |
148 | sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash)); |
bdf225bb |
149 | return xc; |
a39e9541 |
150 | } |
da4432f3 |
151 | |
3001d9ac |
152 | static AV* |
bdf225bb |
153 | mouse_get_xc_wo_check(pTHX_ SV* const metaclass) { |
a39e9541 |
154 | AV* xc; |
a39e9541 |
155 | MAGIC* mg; |
156 | |
157 | if(!IsObject(metaclass)){ |
158 | croak("Not a Mouse metaclass"); |
159 | } |
da4432f3 |
160 | |
a39e9541 |
161 | mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00); |
162 | if(!mg){ |
05e658dc |
163 | /* cache stash for performance */ |
bdf225bb |
164 | HV* const stash = mouse_get_namespace(aTHX_ metaclass); |
a39e9541 |
165 | xc = newAV(); |
da4432f3 |
166 | |
05e658dc |
167 | mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext, |
168 | &mouse_xc_vtbl, NULL, 0); |
a39e9541 |
169 | SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */ |
da4432f3 |
170 | |
a39e9541 |
171 | av_extend(xc, MOUSE_XC_last - 1); |
aa2d2e2c |
172 | |
a5c683f6 |
173 | av_store(xc, MOUSE_XC_GEN, newSVuv(0U)); |
aa2d2e2c |
174 | av_store(xc, MOUSE_XC_STASH, (SV*)stash); |
175 | SvREFCNT_inc_simple_void_NN(stash); |
da4432f3 |
176 | } |
a39e9541 |
177 | else{ |
a39e9541 |
178 | xc = (AV*)MOUSE_mg_obj(mg); |
da4432f3 |
179 | |
a39e9541 |
180 | assert(xc); |
181 | assert(SvTYPE(xc) == SVt_PVAV); |
182 | } |
bdf225bb |
183 | return xc; |
184 | } |
da4432f3 |
185 | |
bdf225bb |
186 | static int |
48a4a7b4 |
187 | mouse_xc_is_fresh(pTHX_ AV* const xc) { |
bdf225bb |
188 | HV* const stash = MOUSE_xc_stash(xc); |
189 | SV* const gen = MOUSE_xc_gen(xc); |
190 | if(SvUVX(gen) != 0U && MOUSE_xc_flags(xc) & MOUSEf_XC_IS_IMMUTABLE) { |
191 | return TRUE; |
a5c683f6 |
192 | } |
bdf225bb |
193 | return SvUVX(gen) == mro_get_pkg_gen(stash); |
194 | } |
a5c683f6 |
195 | |
66aa0bfc |
196 | STATIC_INLINE AV* |
bdf225bb |
197 | mouse_get_xc(pTHX_ SV* const metaclass) { |
198 | AV* const xc = mouse_get_xc_wo_check(aTHX_ metaclass); |
199 | return mouse_xc_is_fresh(aTHX_ xc) |
200 | ? xc |
201 | : mouse_class_update_xc(aTHX_ metaclass, xc); |
202 | } |
a39e9541 |
203 | |
bdf225bb |
204 | static AV* |
205 | mouse_get_xc_if_fresh(pTHX_ SV* const metaclass) { |
206 | AV* const xc = mouse_get_xc_wo_check(aTHX_ metaclass); |
207 | return mouse_xc_is_fresh(aTHX_ xc) |
208 | ? xc |
209 | : NULL; |
a39e9541 |
210 | } |
211 | |
3001d9ac |
212 | static HV* |
074a414d |
213 | mouse_buildargs(pTHX_ SV* metaclass, SV* const klass, I32 ax, I32 items) { |
aa2d2e2c |
214 | HV* args; |
074a414d |
215 | |
216 | /* shift @_ */ |
217 | ax++; |
218 | items--; |
219 | |
220 | if(items == 1){ |
221 | SV* const args_ref = ST(0); |
aa2d2e2c |
222 | if(!IsHashRef(args_ref)){ |
223 | if(!metaclass){ metaclass = get_metaclass(klass); } |
224 | mouse_throw_error(metaclass, NULL, "Single parameters to new() must be a HASH ref"); |
225 | } |
226 | args = newHVhv((HV*)SvRV(args_ref)); |
227 | sv_2mortal((SV*)args); |
228 | } |
229 | else{ |
230 | I32 i; |
231 | |
074a414d |
232 | if( (items % 2) != 0 ){ |
aa2d2e2c |
233 | if(!metaclass){ metaclass = get_metaclass(klass); } |
234 | mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()"); |
235 | } |
236 | |
bc97157e |
237 | args = newHV_mortal(); |
074a414d |
238 | for(i = 0; i < items; i += 2){ |
aa2d2e2c |
239 | (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U); |
240 | } |
241 | |
242 | } |
243 | return args; |
244 | } |
245 | |
3001d9ac |
246 | static void |
e128626c |
247 | mouse_report_unknown_args(pTHX_ SV* const meta, AV* const attrs, HV* const args) { |
248 | HV* const attr_map = newHV_mortal(); |
249 | SV* const unknown = newSVpvs_flags("", SVs_TEMP); |
250 | I32 const len = AvFILLp(attrs) + 1; |
251 | I32 i; |
252 | HE* he; |
253 | |
254 | for(i = 0; i < len; i++){ |
255 | SV* const attr = MOUSE_av_at(attrs, i); |
256 | AV* const xa = mouse_get_xa(aTHX_ attr); |
257 | SV* const init_arg = MOUSE_xa_init_arg(xa); |
258 | if(SvOK(init_arg)){ |
259 | (void)hv_store_ent(attr_map, init_arg, &PL_sv_undef, 0U); |
260 | } |
261 | } |
262 | |
263 | hv_iterinit(args); |
264 | while((he = hv_iternext(args))){ |
265 | SV* const key = hv_iterkeysv(he); |
266 | if(!hv_exists_ent(attr_map, key, 0U)){ |
267 | sv_catpvf(unknown, "%"SVf", ", key); |
268 | } |
269 | } |
270 | |
271 | if(SvCUR(unknown) > 0){ |
272 | SvCUR(unknown) -= 2; /* chop "," */ |
273 | } |
274 | else{ |
275 | sv_setpvs(unknown, "(unknown)"); |
276 | } |
277 | |
278 | mouse_throw_error(meta, NULL, |
279 | "Unknown attribute passed to the constructor of %"SVf": %"SVf, |
280 | mcall0(meta, mouse_name), unknown); |
281 | } |
282 | |
283 | |
284 | |
285 | static void |
cb6a9721 |
286 | mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const is_cloning) { |
d4712779 |
287 | AV* const xc = mouse_get_xc(aTHX_ meta); |
288 | AV* const attrs = MOUSE_xc_attrall(xc); |
60cd9f19 |
289 | I32 const len = AvFILLp(attrs) + 1; |
d4712779 |
290 | I32 i; |
4e7e3250 |
291 | AV* triggers_queue = NULL; |
05089b62 |
292 | I32 used = 0; |
4e7e3250 |
293 | |
074a414d |
294 | assert(meta || object); |
295 | assert(args); |
296 | assert(SvTYPE(args) == SVt_PVHV); |
297 | |
6463e839 |
298 | if(mg_find((SV*)args, PERL_MAGIC_tied)){ |
299 | croak("You cannot use tied HASH reference as initializing arguments"); |
300 | } |
301 | |
e128626c |
302 | /* for each attribute */ |
d4712779 |
303 | for(i = 0; i < len; i++){ |
6bc5b495 |
304 | SV* const attr = MOUSE_av_at(attrs, i); |
305 | AV* const xa = mouse_get_xa(aTHX_ attr); |
4e7e3250 |
306 | |
307 | SV* const slot = MOUSE_xa_slot(xa); |
308 | U16 const flags = (U16)MOUSE_xa_flags(xa); |
309 | SV* const init_arg = MOUSE_xa_init_arg(xa); |
310 | HE* he; |
311 | |
312 | if(SvOK(init_arg) && ( he = hv_fetch_ent(args, init_arg, FALSE, 0U) ) ){ |
313 | SV* value = HeVAL(he); |
314 | if(flags & MOUSEf_ATTR_HAS_TC){ |
315 | value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags); |
316 | } |
ca8e67d6 |
317 | value = set_slot(object, slot, value); |
f1575d9f |
318 | if(flags & MOUSEf_ATTR_IS_WEAK_REF && SvROK(value)){ |
4e7e3250 |
319 | weaken_slot(object, slot); |
320 | } |
cb6a9721 |
321 | if(flags & MOUSEf_ATTR_HAS_TRIGGER){ |
4e7e3250 |
322 | AV* const pair = newAV(); |
323 | av_push(pair, newSVsv( mcall0s(attr, "trigger") )); |
324 | av_push(pair, newSVsv(value)); |
325 | |
cb6a9721 |
326 | if(!triggers_queue) { |
327 | triggers_queue = newAV_mortal(); |
328 | } |
4e7e3250 |
329 | av_push(triggers_queue, (SV*)pair); |
330 | } |
e128626c |
331 | used++; |
4e7e3250 |
332 | } |
6c7491f2 |
333 | else { /* no init arg */ |
4e7e3250 |
334 | if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){ |
6c7491f2 |
335 | /* skip if the object has the slot (it occurs on cloning/reblessing) */ |
336 | if(!(flags & MOUSEf_ATTR_IS_LAZY) && !has_slot(object, slot)){ |
4e7e3250 |
337 | mouse_xa_set_default(aTHX_ xa, object); |
338 | } |
339 | } |
f1575d9f |
340 | else if(is_cloning) { |
341 | if(flags & MOUSEf_ATTR_IS_WEAK_REF){ |
342 | SV* const value = get_slot(object, slot); |
343 | if(SvROK(value)) { |
344 | weaken_slot(object, slot); |
345 | } |
346 | } |
347 | } |
348 | /* don't check "required" while cloning (or reblesseing) */ |
349 | else if(flags & MOUSEf_ATTR_IS_REQUIRED) { |
4e7e3250 |
350 | mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot); |
351 | } |
352 | } |
e128626c |
353 | } /* for each attribute */ |
354 | |
05089b62 |
355 | if(MOUSE_xc_flags(xc) & MOUSEf_XC_CONSTRUCTOR_IS_STRICT |
356 | && used < (I32)HvUSEDKEYS(args)){ |
e128626c |
357 | mouse_report_unknown_args(aTHX_ meta, attrs, args); |
358 | } |
4e7e3250 |
359 | |
360 | if(triggers_queue){ |
60cd9f19 |
361 | I32 const len = AvFILLp(triggers_queue) + 1; |
4e7e3250 |
362 | for(i = 0; i < len; i++){ |
363 | AV* const pair = (AV*)AvARRAY(triggers_queue)[i]; |
364 | SV* const trigger = AvARRAY(pair)[0]; |
365 | SV* const value = AvARRAY(pair)[1]; |
366 | |
367 | mcall1(object, trigger, value); |
368 | } |
369 | } |
370 | |
371 | if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){ |
ca8e67d6 |
372 | (void)set_slot(object, newSVpvs_flags("__METACLASS__", SVs_TEMP), meta); |
d4712779 |
373 | } |
aa2d2e2c |
374 | } |
375 | |
f6c81f00 |
376 | STATIC_INLINE SV* |
1bbf8369 |
377 | mouse_initialize_metaclass(pTHX_ SV* const klass) { |
f6c81f00 |
378 | SV* const meta = get_metaclass(klass); |
379 | if(LIKELY(SvOK(meta))){ |
380 | return meta; |
1bbf8369 |
381 | } |
f6c81f00 |
382 | return mcall1s(newSVpvs_flags("Mouse::Meta::Class", SVs_TEMP), |
383 | "initialize", klass); |
1bbf8369 |
384 | } |
385 | |
adb5eb76 |
386 | static void |
387 | mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) { |
388 | AV* const buildall = MOUSE_xc_buildall(xc); |
389 | I32 const len = AvFILLp(buildall) + 1; |
390 | I32 i; |
391 | for(i = 0; i < len; i++){ |
392 | dSP; |
393 | |
394 | PUSHMARK(SP); |
395 | EXTEND(SP, 2); |
396 | PUSHs(object); |
397 | PUSHs(args); |
398 | PUTBACK; |
399 | |
b3cd4c14 |
400 | call_sv_safe(AvARRAY(buildall)[i], G_VOID); |
accc9fc2 |
401 | |
402 | /* discard a scalar which G_VOID returns */ |
403 | SPAGAIN; |
404 | (void)POPs; |
405 | PUTBACK; |
adb5eb76 |
406 | } |
407 | } |
408 | |
cb60d0b5 |
409 | static AV* |
410 | mouse_get_modifier_storage(pTHX_ |
411 | SV* const meta, |
412 | enum mouse_modifier_t const m, SV* const name) { |
413 | static const char* const keys[] = { |
414 | "before", |
415 | "around", |
416 | "after", |
417 | }; |
418 | SV* const key = sv_2mortal(Perl_newSVpvf(aTHX_ "%s_method_modifiers", keys[m])); |
419 | SV* table; |
420 | SV* storage_ref; |
421 | |
d06d9266 |
422 | must_defined(name, "a method name"); |
cb60d0b5 |
423 | |
424 | table = get_slot(meta, key); |
425 | |
426 | if(!table){ |
427 | /* $meta->{$key} = {} */ |
428 | table = sv_2mortal(newRV_noinc((SV*)newHV())); |
429 | set_slot(meta, key, table); |
430 | } |
431 | |
432 | storage_ref = get_slot(table, name); |
433 | |
434 | if(!storage_ref){ |
435 | storage_ref = sv_2mortal(newRV_noinc((SV*)newAV())); |
436 | set_slot(table, name, storage_ref); |
437 | } |
438 | else{ |
439 | if(!IsArrayRef(storage_ref)){ |
440 | croak("Modifier strorage for '%s' is not an ARRAY reference", keys[m]); |
441 | } |
442 | } |
443 | |
444 | return (AV*)SvRV(storage_ref); |
445 | } |
446 | |
91ee66cb |
447 | static |
448 | XSPROTO(XS_Mouse_value_holder) { |
902174eb |
449 | dVAR; dXSARGS; |
450 | SV* const value = (SV*)XSANY.any_ptr; |
451 | assert(value); |
452 | PERL_UNUSED_VAR(items); |
453 | ST(0) = value; |
454 | XSRETURN(1); |
455 | } |
456 | |
fe5044b1 |
457 | DECL_BOOT(Mouse__Util); |
458 | DECL_BOOT(Mouse__Util__TypeConstraints); |
459 | DECL_BOOT(Mouse__Meta__Method__Accessor__XS); |
460 | DECL_BOOT(Mouse__Meta__Attribute); |
461 | |
646c0371 |
462 | MODULE = Mouse PACKAGE = Mouse |
df6dd016 |
463 | |
464 | PROTOTYPES: DISABLE |
465 | |
cccb83de |
466 | BOOT: |
902174eb |
467 | { |
cccb83de |
468 | mouse_package = newSVpvs_share("package"); |
469 | mouse_namespace = newSVpvs_share("namespace"); |
3e44140b |
470 | mouse_methods = newSVpvs_share("methods"); |
a5df48e5 |
471 | mouse_name = newSVpvs_share("name"); |
d39ac61c |
472 | mouse_coerce = newSVpvs_share("coerce"); |
3e44140b |
473 | |
047d7af0 |
474 | mouse_get_attribute = newSVpvs_share("get_attribute"); |
475 | mouse_get_attribute_list = newSVpvs_share("get_attribute_list"); |
da4432f3 |
476 | |
fe5044b1 |
477 | CALL_BOOT(Mouse__Util); |
478 | CALL_BOOT(Mouse__Util__TypeConstraints); |
479 | CALL_BOOT(Mouse__Meta__Method__Accessor__XS); |
480 | CALL_BOOT(Mouse__Meta__Attribute); |
902174eb |
481 | } |
cccb83de |
482 | |
7d96ae4d |
483 | MODULE = Mouse PACKAGE = Mouse::Meta::Module |
484 | |
485 | BOOT: |
486 | INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package); |
487 | INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods); |
488 | INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes); |
489 | |
490 | HV* |
491 | namespace(SV* self) |
492 | CODE: |
493 | { |
05e658dc |
494 | RETVAL = mouse_get_namespace(aTHX_ self); |
7d96ae4d |
495 | } |
496 | OUTPUT: |
497 | RETVAL |
498 | |
3e44140b |
499 | # ignore extra arguments for extensibility |
500 | void |
501 | add_method(SV* self, SV* name, SV* code, ...) |
502 | CODE: |
503 | { |
6a97bbda |
504 | SV* const package = get_slot(self, mouse_package); /* $self->{package} */ |
505 | SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */ |
3e44140b |
506 | GV* gv; |
507 | SV* code_ref; |
508 | |
509 | if(!(package && SvOK(package))){ |
510 | croak("No package name defined"); |
511 | } |
512 | |
d06d9266 |
513 | must_defined(name, "a method name"); |
514 | must_ref (code, "a CODE reference", SVt_NULL); /* any reftype is OK */ |
3e44140b |
515 | |
516 | code_ref = code; |
517 | if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){ |
518 | SV* sv = code_ref; /* used in tryAMAGICunDEREF */ |
519 | SV** sp = &sv; /* used in tryAMAGICunDEREF */ |
520 | tryAMAGICunDEREF(to_cv); /* try \&{$code} */ |
d06d9266 |
521 | must_ref(code, "a CODE reference", SVt_PVCV); |
3e44140b |
522 | code_ref = sv; |
523 | } |
524 | |
525 | /* *{$package . '::' . $name} -> *gv */ |
526 | gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV); |
d67f600d |
527 | mouse_install_sub(aTHX_ gv, code_ref); |
9acbbf0a |
528 | /* CvMETHOD_on((CV*)SvRV(code_ref)); */ |
ca8e67d6 |
529 | (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */ |
3e44140b |
530 | } |
531 | |
43165725 |
532 | MODULE = Mouse PACKAGE = Mouse::Meta::Class |
533 | |
534 | BOOT: |
902174eb |
535 | { |
536 | CV* xsub; |
537 | |
43165725 |
538 | INSTALL_SIMPLE_READER(Class, roles); |
539 | INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id); |
80efe911 |
540 | INSTALL_SIMPLE_READER(Class, is_immutable); |
3a2ccd77 |
541 | |
fad99075 |
542 | INSTALL_INHERITABLE_CLASS_ACCESSOR(strict_constructor); |
543 | |
787f84c2 |
544 | INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method"); |
545 | INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute"); |
546 | INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS"); |
547 | INSTALL_CLASS_HOLDER(Class, destructor_class, "Mouse::Meta::Method::Destructor::XS"); |
a5c683f6 |
548 | |
902174eb |
549 | xsub = newXS("Mouse::Meta::Method::Constructor::XS::_generate_constructor", |
550 | XS_Mouse_value_holder, file); |
551 | CvXSUBANY(xsub).any_ptr |
552 | = newRV_inc((SV*)get_cvs("Mouse::Object::new", GV_ADD)); |
553 | |
554 | xsub = newXS("Mouse::Meta::Method::Destructor::XS::_generate_destructor", |
555 | XS_Mouse_value_holder, file); |
556 | CvXSUBANY(xsub).any_ptr |
557 | = newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", GV_ADD)); |
558 | } |
559 | |
a5c683f6 |
560 | |
cccb83de |
561 | void |
562 | linearized_isa(SV* self) |
563 | PPCODE: |
564 | { |
05e658dc |
565 | /* MOUSE_xc_stash() is not available because the xc system depends on |
566 | linearized_isa() */ |
567 | HV* const stash = mouse_get_namespace(aTHX_ self); |
568 | AV* const linearized_isa = mro_get_linear_isa(stash); |
e0f85c26 |
569 | I32 const len = AvFILLp(linearized_isa) + 1; |
cccb83de |
570 | I32 i; |
cccb83de |
571 | EXTEND(SP, len); |
572 | for(i = 0; i < len; i++){ |
573 | PUSHs(AvARRAY(linearized_isa)[i]); |
574 | } |
575 | } |
576 | |
da4432f3 |
577 | void |
578 | get_all_attributes(SV* self) |
579 | PPCODE: |
580 | { |
0aad0266 |
581 | AV* const xc = mouse_get_xc(aTHX_ self); |
ae34e077 |
582 | AV* const all_attrs = MOUSE_xc_attrall(xc); |
da4432f3 |
583 | I32 const len = AvFILLp(all_attrs) + 1; |
584 | I32 i; |
585 | |
586 | EXTEND(SP, len); |
587 | for(i = 0; i < len; i++){ |
588 | PUSHs( MOUSE_av_at(all_attrs, i) ); |
589 | } |
590 | } |
441964ce |
591 | |
fcdc12ac |
592 | void |
ba153b33 |
593 | new_object(SV* meta, ...) |
aa2d2e2c |
594 | CODE: |
595 | { |
aa2d2e2c |
596 | AV* const xc = mouse_get_xc(aTHX_ meta); |
074a414d |
597 | HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items); |
fcdc12ac |
598 | SV* object; |
d4712779 |
599 | |
fcdc12ac |
600 | object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); |
601 | mouse_class_initialize_object(aTHX_ meta, object, args, FALSE); |
e0f85c26 |
602 | mouse_buildall(aTHX_ xc, object, sv_2mortal(newRV_inc((SV*)args))); |
fcdc12ac |
603 | ST(0) = object; /* because object is mortal, we should return it as is */ |
604 | XSRETURN(1); |
aa2d2e2c |
605 | } |
aa2d2e2c |
606 | |
fcdc12ac |
607 | void |
13b99908 |
608 | clone_object(SV* meta, SV* object, ...) |
609 | CODE: |
610 | { |
611 | AV* const xc = mouse_get_xc(aTHX_ meta); |
612 | HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax + 1, items - 1); |
fcdc12ac |
613 | SV* proto; |
13b99908 |
614 | |
615 | if(!mouse_is_an_instance_of(aTHX_ MOUSE_xc_stash(xc), object)) { |
616 | mouse_throw_error(meta, object, |
617 | "You must pass an instance of the metaclass (%"SVf"), not (%"SVf")", |
618 | mcall0(meta, mouse_name), object); |
619 | } |
620 | |
fcdc12ac |
621 | proto = mouse_instance_clone(aTHX_ object); |
622 | mouse_class_initialize_object(aTHX_ meta, proto, args, TRUE); |
623 | ST(0) = proto; /* because object is mortal, we should return it as is */ |
624 | XSRETURN(1); |
13b99908 |
625 | } |
13b99908 |
626 | |
aa2d2e2c |
627 | void |
cb6a9721 |
628 | _initialize_object(SV* meta, SV* object, HV* args, bool is_cloning = FALSE) |
aa2d2e2c |
629 | CODE: |
630 | { |
cb6a9721 |
631 | mouse_class_initialize_object(aTHX_ meta, object, args, is_cloning); |
aa2d2e2c |
632 | } |
633 | |
6f09819f |
634 | void |
635 | _invalidate_metaclass_cache(SV* meta) |
636 | CODE: |
637 | { |
638 | AV* const xc = mouse_get_xc_if_fresh(aTHX_ meta); |
639 | if(xc) { |
640 | SV* const gen = MOUSE_xc_gen(xc); |
641 | sv_setuv(gen, 0U); |
642 | } |
643 | delete_slot(meta, newSVpvs_flags("_mouse_cache_", SVs_TEMP)); |
644 | } |
645 | |
646 | |
43165725 |
647 | MODULE = Mouse PACKAGE = Mouse::Meta::Role |
648 | |
649 | BOOT: |
650 | INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles); |
651 | INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id); |
652 | |
787f84c2 |
653 | INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method"); |
e058b279 |
654 | |
cb60d0b5 |
655 | void |
656 | add_before_modifier(SV* self, SV* name, SV* modifier) |
657 | CODE: |
658 | { |
fe5044b1 |
659 | av_push(mouse_get_modifier_storage(aTHX_ self, (enum mouse_modifier_t)ix, name), newSVsv(modifier)); |
cb60d0b5 |
660 | } |
661 | ALIAS: |
662 | add_before_method_modifier = MOUSE_M_BEFORE |
663 | add_around_method_modifier = MOUSE_M_AROUND |
664 | add_after_method_modifier = MOUSE_M_AFTER |
665 | |
666 | void |
667 | get_before_modifiers(SV* self, SV* name) |
668 | ALIAS: |
669 | get_before_method_modifiers = MOUSE_M_BEFORE |
670 | get_around_method_modifiers = MOUSE_M_AROUND |
671 | get_after_method_modifiers = MOUSE_M_AFTER |
672 | PPCODE: |
673 | { |
e0f85c26 |
674 | AV* const storage = mouse_get_modifier_storage(aTHX_ self, |
675 | (enum mouse_modifier_t)ix, name); |
cb60d0b5 |
676 | I32 const len = av_len(storage) + 1; |
677 | if(GIMME_V == G_ARRAY) { |
678 | I32 i; |
679 | EXTEND(SP, len); |
680 | for(i = 0; i < len; i++){ |
681 | PUSHs(*av_fetch(storage, i, TRUE)); |
682 | } |
683 | } |
684 | else{ |
685 | mPUSHi(len); |
686 | } |
687 | } |
688 | |
b4dc9315 |
689 | void |
690 | add_metaclass_accessor(SV* self, SV* name) |
691 | CODE: |
692 | { |
4bc0a681 |
693 | SV* const klass = mcall0(self, mouse_name); |
b4dc9315 |
694 | const char* fq_name = form("%"SVf"::%"SVf, klass, name); |
695 | STRLEN keylen; |
696 | const char* const key = SvPV_const(name, keylen); |
697 | mouse_simple_accessor_generate(aTHX_ fq_name, key, keylen, |
698 | XS_Mouse_inheritable_class_accessor, NULL, 0); |
699 | } |
700 | |
aa2d2e2c |
701 | MODULE = Mouse PACKAGE = Mouse::Object |
43165725 |
702 | |
fcdc12ac |
703 | void |
074a414d |
704 | new(SV* klass, ...) |
705 | CODE: |
706 | { |
1bbf8369 |
707 | SV* const meta = mouse_initialize_metaclass(aTHX_ klass); |
074a414d |
708 | AV* const xc = mouse_get_xc(aTHX_ meta); |
709 | UV const flags = MOUSE_xc_flags(xc); |
710 | SV* args; |
fcdc12ac |
711 | SV* object; |
074a414d |
712 | |
713 | /* BUILDARGS */ |
714 | if(flags & MOUSEf_XC_HAS_BUILDARGS){ |
adb5eb76 |
715 | I32 i; |
074a414d |
716 | SPAGAIN; |
717 | |
718 | PUSHMARK(SP); |
719 | EXTEND(SP, items); |
720 | for(i = 0; i < items; i++){ |
721 | PUSHs(ST(i)); |
722 | } |
b3cd4c14 |
723 | |
074a414d |
724 | PUTBACK; |
b3cd4c14 |
725 | call_method_safes("BUILDARGS", G_SCALAR); |
726 | |
074a414d |
727 | SPAGAIN; |
728 | args = POPs; |
729 | PUTBACK; |
730 | |
731 | if(!IsHashRef(args)){ |
732 | croak("BUILDARGS did not return a HASH reference"); |
733 | } |
734 | } |
735 | else{ |
736 | args = newRV_inc((SV*)mouse_buildargs(aTHX_ meta, klass, ax, items)); |
737 | sv_2mortal(args); |
738 | } |
739 | |
740 | /* new_object */ |
fcdc12ac |
741 | object = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); |
742 | mouse_class_initialize_object(aTHX_ meta, object, (HV*)SvRV(args), FALSE); |
e0f85c26 |
743 | /* BUILDALL */ |
744 | mouse_buildall(aTHX_ xc, object, args); |
fcdc12ac |
745 | ST(0) = object; /* because object is mortal, we should return it as is */ |
746 | XSRETURN(1); |
074a414d |
747 | } |
074a414d |
748 | |
a5c683f6 |
749 | void |
750 | DESTROY(SV* object) |
adb5eb76 |
751 | ALIAS: |
752 | DESTROY = 0 |
753 | DEMOLISHALL = 1 |
aa2d2e2c |
754 | CODE: |
755 | { |
a5c683f6 |
756 | SV* const meta = get_metaclass(object); |
bdf225bb |
757 | AV* xc; |
a5c683f6 |
758 | AV* demolishall; |
bdf225bb |
759 | I32 len; |
760 | I32 i; |
a5c683f6 |
761 | |
762 | if(!IsObject(object)){ |
31113a4a |
763 | croak("You must not call %s as a class method", |
764 | ix == 0 ? "DESTROY" : "DEMOLISHALL"); |
a5c683f6 |
765 | } |
766 | |
bdf225bb |
767 | if(SvOK(meta) && (xc = mouse_get_xc_if_fresh(aTHX_ meta))) { |
a5c683f6 |
768 | demolishall = MOUSE_xc_demolishall(xc); |
769 | } |
adb5eb76 |
770 | else { /* The metaclass is already destroyed */ |
a5c683f6 |
771 | AV* const linearized_isa = mro_get_linear_isa(SvSTASH(SvRV(object))); |
7efbc77d |
772 | |
a5c683f6 |
773 | len = AvFILLp(linearized_isa) + 1; |
7efbc77d |
774 | |
a5c683f6 |
775 | demolishall = newAV_mortal(); |
776 | for(i = 0; i < len; i++){ |
777 | SV* const klass = MOUSE_av_at(linearized_isa, i); |
778 | HV* const st = gv_stashsv(klass, TRUE); |
779 | GV* const gv = stash_fetchs(st, "DEMOLISH", FALSE); |
780 | if(gv && GvCVu(gv)){ |
781 | av_push(demolishall, newRV_inc((SV*)GvCV(gv))); |
782 | } |
783 | } |
784 | } |
785 | |
bdf225bb |
786 | len = AvFILLp(demolishall) + 1; |
a5c683f6 |
787 | if(len > 0){ |
5bc4e0be |
788 | SV* const in_global_destruction = boolSV(PL_dirty); |
789 | SAVEI32(PL_statusvalue); /* local $? */ |
9bd5683d |
790 | PL_statusvalue = 0; |
70425827 |
791 | |
a5c683f6 |
792 | SAVESPTR(ERRSV); /* local $@ */ |
b3cd4c14 |
793 | ERRSV = sv_newmortal(); |
794 | |
795 | EXTEND(SP, 2); |
a5c683f6 |
796 | |
a5c683f6 |
797 | for(i = 0; i < len; i++){ |
f8beb873 |
798 | SPAGAIN; |
a5c683f6 |
799 | |
800 | PUSHMARK(SP); |
b3cd4c14 |
801 | PUSHs(object); |
5bc4e0be |
802 | PUSHs(in_global_destruction); |
a5c683f6 |
803 | PUTBACK; |
804 | |
accc9fc2 |
805 | call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL); |
806 | |
807 | /* discard a scalar which G_VOID returns */ |
808 | SPAGAIN; |
809 | (void)POPs; |
810 | PUTBACK; |
811 | |
6ad77996 |
812 | if(sv_true(ERRSV)){ |
a5c683f6 |
813 | SV* const e = newSVsv(ERRSV); |
814 | |
815 | FREETMPS; |
816 | LEAVE; |
817 | |
818 | sv_setsv(ERRSV, e); |
819 | SvREFCNT_dec(e); |
820 | croak(NULL); /* rethrow */ |
821 | } |
822 | } |
823 | } |
824 | } |
825 | |
826 | HV* |
827 | BUILDARGS(SV* klass, ...) |
7efbc77d |
828 | CODE: |
829 | { |
a5c683f6 |
830 | RETVAL = mouse_buildargs(aTHX_ NULL, klass, ax, items); |
7efbc77d |
831 | } |
832 | OUTPUT: |
833 | RETVAL |
834 | |
a5c683f6 |
835 | |
adb5eb76 |
836 | void |
837 | BUILDALL(SV* self, SV* args) |
838 | CODE: |
839 | { |
f50e50d6 |
840 | SV* const meta = get_metaclass(self); |
841 | AV* const xc = mouse_get_xc(aTHX_ meta); |
adb5eb76 |
842 | |
d06d9266 |
843 | must_ref(args, "a HASH reference to BUILDALL", SVt_PVHV); |
adb5eb76 |
844 | mouse_buildall(aTHX_ xc, self, args); |
845 | } |