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 | |
44 | static MGVTBL mouse_xc_vtbl; /* for identity */ |
45 | |
46 | static void |
47 | mouse_class_push_attribute_list(pTHX_ SV* const metaclass, AV* const attrall, HV* const seen){ |
48 | dSP; |
49 | I32 n; |
50 | |
51 | /* $meta->get_attribute_list */ |
52 | PUSHMARK(SP); |
53 | XPUSHs(metaclass); |
54 | PUTBACK; |
55 | |
047d7af0 |
56 | n = call_sv(mouse_get_attribute_list, G_ARRAY | G_METHOD); |
a39e9541 |
57 | for(NOOP; n > 0; n--){ |
58 | SV* name; |
59 | |
60 | SPAGAIN; |
61 | name = POPs; |
62 | PUTBACK; |
63 | |
64 | if(hv_exists_ent(seen, name, 0U)){ |
65 | continue; |
66 | } |
67 | (void)hv_store_ent(seen, name, &PL_sv_undef, 0U); |
da4432f3 |
68 | |
047d7af0 |
69 | av_push(attrall, newSVsv( mcall1(metaclass, mouse_get_attribute, name) )); |
a39e9541 |
70 | } |
71 | } |
743ca82e |
72 | |
aa2d2e2c |
73 | static int |
74 | mouse_class_has_custom_buildargs(pTHX_ HV* const stash) { |
75 | XS(XS_Mouse__Object_BUILDARGS); /* prototype */ |
76 | |
77 | GV* const buildargs = gv_fetchmeth_autoload(stash, "BUILDARGS", sizeof("BUILDARGS")-1, 0); |
aa2d2e2c |
78 | |
12872cc1 |
79 | return buildargs && CvXSUB(GvCV(buildargs)) != XS_Mouse__Object_BUILDARGS; |
aa2d2e2c |
80 | } |
81 | |
a39e9541 |
82 | static void |
83 | mouse_class_update_xc(pTHX_ SV* const metaclass PERL_UNUSED_DECL, HV* const stash, AV* const xc) { |
84 | AV* const linearized_isa = mro_get_linear_isa(stash); |
85 | I32 const len = AvFILLp(linearized_isa); |
86 | I32 i; |
aa2d2e2c |
87 | U32 flags = 0x00; |
a39e9541 |
88 | AV* const attrall = newAV(); |
89 | AV* const buildall = newAV(); |
90 | AV* const demolishall = newAV(); |
91 | HV* const seen = newHV(); /* for attributes */ |
da4432f3 |
92 | |
a39e9541 |
93 | ENTER; |
94 | SAVETMPS; |
da4432f3 |
95 | |
a39e9541 |
96 | sv_2mortal((SV*)seen); |
da4432f3 |
97 | |
a39e9541 |
98 | /* old data will be delete at the end of the perl scope */ |
99 | av_delete(xc, MOUSE_XC_DEMOLISHALL, 0x00); |
100 | av_delete(xc, MOUSE_XC_BUILDALL, 0x00); |
101 | av_delete(xc, MOUSE_XC_ATTRALL, 0x00); |
743ca82e |
102 | |
a39e9541 |
103 | SvREFCNT_inc_simple_void_NN(linearized_isa); |
104 | sv_2mortal((SV*)linearized_isa); |
da4432f3 |
105 | |
a39e9541 |
106 | /* update */ |
da4432f3 |
107 | |
4e7e3250 |
108 | if(predicate_calls(metaclass, "is_immutable")){ |
aa2d2e2c |
109 | flags |= MOUSEf_XC_IS_IMMUTABLE; |
110 | } |
111 | |
4e7e3250 |
112 | if(predicate_calls(metaclass, "is_anon_class")){ |
113 | flags |= MOUSEf_XC_IS_ANON; |
114 | } |
115 | |
aa2d2e2c |
116 | if(mouse_class_has_custom_buildargs(aTHX_ stash)){ |
117 | flags |= MOUSEf_XC_HAS_BUILDARGS; |
118 | } |
119 | |
e128626c |
120 | if(predicate_calls(metaclass, "__strict_constructor")){ |
121 | flags |= MOUSEf_XC_CONSTRUCTOR_IS_STRICT; |
122 | } |
123 | |
4e7e3250 |
124 | av_store(xc, MOUSE_XC_FLAGS, newSVuv(flags)); |
a39e9541 |
125 | av_store(xc, MOUSE_XC_ATTRALL, (SV*)attrall); |
126 | av_store(xc, MOUSE_XC_BUILDALL, (SV*)buildall); |
127 | av_store(xc, MOUSE_XC_DEMOLISHALL, (SV*)demolishall); |
da4432f3 |
128 | |
a39e9541 |
129 | for(i = 0; i < len; i++){ |
047d7af0 |
130 | SV* const klass = MOUSE_av_at(linearized_isa, i); |
074a414d |
131 | HV* const st = gv_stashsv(klass, TRUE); |
a39e9541 |
132 | SV* meta; |
133 | GV* gv; |
da4432f3 |
134 | |
074a414d |
135 | gv = stash_fetchs(st, "BUILD", FALSE); |
a39e9541 |
136 | if(gv && GvCVu(gv)){ |
074a414d |
137 | av_unshift(buildall, 1); |
138 | av_store(buildall, 0, newRV_inc((SV*)GvCV(gv))); |
a39e9541 |
139 | } |
da4432f3 |
140 | |
074a414d |
141 | gv = stash_fetchs(st, "DEMOLISH", FALSE); |
a39e9541 |
142 | if(gv && GvCVu(gv)){ |
143 | av_push(demolishall, newRV_inc((SV*)GvCV(gv))); |
144 | } |
da4432f3 |
145 | |
a39e9541 |
146 | /* ATTRIBUTES */ |
aa2d2e2c |
147 | meta = get_metaclass(klass); |
a39e9541 |
148 | if(!SvOK(meta)){ |
149 | continue; /* skip non-Mouse classes */ |
150 | } |
da4432f3 |
151 | |
a39e9541 |
152 | mouse_class_push_attribute_list(aTHX_ meta, attrall, seen); |
153 | } |
da4432f3 |
154 | |
a39e9541 |
155 | FREETMPS; |
156 | LEAVE; |
da4432f3 |
157 | |
a39e9541 |
158 | sv_setuv(MOUSE_xc_gen(xc), mro_get_pkg_gen(stash)); |
159 | } |
da4432f3 |
160 | |
3001d9ac |
161 | static AV* |
a39e9541 |
162 | mouse_get_xc(pTHX_ SV* const metaclass) { |
163 | AV* xc; |
164 | SV* gen; |
165 | HV* stash; |
166 | MAGIC* mg; |
167 | |
168 | if(!IsObject(metaclass)){ |
169 | croak("Not a Mouse metaclass"); |
170 | } |
da4432f3 |
171 | |
a39e9541 |
172 | mg = mouse_mg_find(aTHX_ SvRV(metaclass), &mouse_xc_vtbl, 0x00); |
173 | if(!mg){ |
174 | SV* const package = get_slot(metaclass, mouse_package); |
aa2d2e2c |
175 | STRLEN len; |
176 | const char* const pv = SvPV_const(package, len); |
da4432f3 |
177 | |
aa2d2e2c |
178 | stash = gv_stashpvn(pv, len, TRUE); |
a39e9541 |
179 | xc = newAV(); |
da4432f3 |
180 | |
aa2d2e2c |
181 | mg = sv_magicext(SvRV(metaclass), (SV*)xc, PERL_MAGIC_ext, &mouse_xc_vtbl, pv, len); |
a39e9541 |
182 | SvREFCNT_dec(xc); /* refcnt++ in sv_magicext */ |
da4432f3 |
183 | |
a39e9541 |
184 | av_extend(xc, MOUSE_XC_last - 1); |
aa2d2e2c |
185 | |
a5c683f6 |
186 | av_store(xc, MOUSE_XC_GEN, newSVuv(0U)); |
aa2d2e2c |
187 | av_store(xc, MOUSE_XC_STASH, (SV*)stash); |
4e7e3250 |
188 | |
aa2d2e2c |
189 | SvREFCNT_inc_simple_void_NN(stash); |
da4432f3 |
190 | } |
a39e9541 |
191 | else{ |
a39e9541 |
192 | xc = (AV*)MOUSE_mg_obj(mg); |
da4432f3 |
193 | |
a39e9541 |
194 | assert(xc); |
195 | assert(SvTYPE(xc) == SVt_PVAV); |
196 | } |
da4432f3 |
197 | |
aa2d2e2c |
198 | gen = MOUSE_xc_gen(xc); |
a5c683f6 |
199 | |
200 | if(SvUVX(gen) != 0U && MOUSE_xc_flags(xc) & MOUSEf_XC_IS_IMMUTABLE){ |
201 | return xc; |
202 | } |
203 | |
aa2d2e2c |
204 | stash = MOUSE_xc_stash(xc); |
205 | |
a5c683f6 |
206 | if(SvUVX(gen) != mro_get_pkg_gen(stash)){ |
a39e9541 |
207 | mouse_class_update_xc(aTHX_ metaclass, stash, xc); |
da4432f3 |
208 | } |
a39e9541 |
209 | |
210 | return xc; |
211 | } |
212 | |
3001d9ac |
213 | static HV* |
074a414d |
214 | mouse_buildargs(pTHX_ SV* metaclass, SV* const klass, I32 ax, I32 items) { |
aa2d2e2c |
215 | HV* args; |
074a414d |
216 | |
217 | /* shift @_ */ |
218 | ax++; |
219 | items--; |
220 | |
221 | if(items == 1){ |
222 | SV* const args_ref = ST(0); |
aa2d2e2c |
223 | if(!IsHashRef(args_ref)){ |
224 | if(!metaclass){ metaclass = get_metaclass(klass); } |
225 | mouse_throw_error(metaclass, NULL, "Single parameters to new() must be a HASH ref"); |
226 | } |
227 | args = newHVhv((HV*)SvRV(args_ref)); |
228 | sv_2mortal((SV*)args); |
229 | } |
230 | else{ |
231 | I32 i; |
232 | |
6fab29c6 |
233 | args = newHV_mortal(); |
aa2d2e2c |
234 | |
074a414d |
235 | if( (items % 2) != 0 ){ |
aa2d2e2c |
236 | if(!metaclass){ metaclass = get_metaclass(klass); } |
237 | mouse_throw_error(metaclass, NULL, "Odd number of parameters to new()"); |
238 | } |
239 | |
074a414d |
240 | for(i = 0; i < items; i += 2){ |
aa2d2e2c |
241 | (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U); |
242 | } |
243 | |
244 | } |
245 | return args; |
246 | } |
247 | |
3001d9ac |
248 | static void |
e128626c |
249 | mouse_report_unknown_args(pTHX_ SV* const meta, AV* const attrs, HV* const args) { |
250 | HV* const attr_map = newHV_mortal(); |
251 | SV* const unknown = newSVpvs_flags("", SVs_TEMP); |
252 | I32 const len = AvFILLp(attrs) + 1; |
253 | I32 i; |
254 | HE* he; |
255 | |
256 | for(i = 0; i < len; i++){ |
257 | SV* const attr = MOUSE_av_at(attrs, i); |
258 | AV* const xa = mouse_get_xa(aTHX_ attr); |
259 | SV* const init_arg = MOUSE_xa_init_arg(xa); |
260 | if(SvOK(init_arg)){ |
261 | (void)hv_store_ent(attr_map, init_arg, &PL_sv_undef, 0U); |
262 | } |
263 | } |
264 | |
265 | hv_iterinit(args); |
266 | while((he = hv_iternext(args))){ |
267 | SV* const key = hv_iterkeysv(he); |
268 | if(!hv_exists_ent(attr_map, key, 0U)){ |
269 | sv_catpvf(unknown, "%"SVf", ", key); |
270 | } |
271 | } |
272 | |
273 | if(SvCUR(unknown) > 0){ |
274 | SvCUR(unknown) -= 2; /* chop "," */ |
275 | } |
276 | else{ |
277 | sv_setpvs(unknown, "(unknown)"); |
278 | } |
279 | |
280 | mouse_throw_error(meta, NULL, |
281 | "Unknown attribute passed to the constructor of %"SVf": %"SVf, |
282 | mcall0(meta, mouse_name), unknown); |
283 | } |
284 | |
285 | |
286 | |
287 | static void |
d4712779 |
288 | mouse_class_initialize_object(pTHX_ SV* const meta, SV* const object, HV* const args, bool const ignore_triggers) { |
289 | AV* const xc = mouse_get_xc(aTHX_ meta); |
290 | AV* const attrs = MOUSE_xc_attrall(xc); |
4e7e3250 |
291 | I32 len = AvFILLp(attrs) + 1; |
d4712779 |
292 | I32 i; |
4e7e3250 |
293 | AV* triggers_queue = NULL; |
e128626c |
294 | I32 used = 0; |
4e7e3250 |
295 | |
074a414d |
296 | assert(meta || object); |
297 | assert(args); |
298 | assert(SvTYPE(args) == SVt_PVHV); |
299 | |
6463e839 |
300 | if(mg_find((SV*)args, PERL_MAGIC_tied)){ |
301 | croak("You cannot use tied HASH reference as initializing arguments"); |
302 | } |
303 | |
4e7e3250 |
304 | if(!ignore_triggers){ |
305 | triggers_queue = newAV_mortal(); |
306 | } |
0aad0266 |
307 | |
e128626c |
308 | /* for each attribute */ |
d4712779 |
309 | for(i = 0; i < len; i++){ |
6bc5b495 |
310 | SV* const attr = MOUSE_av_at(attrs, i); |
311 | AV* const xa = mouse_get_xa(aTHX_ attr); |
4e7e3250 |
312 | |
313 | SV* const slot = MOUSE_xa_slot(xa); |
314 | U16 const flags = (U16)MOUSE_xa_flags(xa); |
315 | SV* const init_arg = MOUSE_xa_init_arg(xa); |
316 | HE* he; |
317 | |
318 | if(SvOK(init_arg) && ( he = hv_fetch_ent(args, init_arg, FALSE, 0U) ) ){ |
319 | SV* value = HeVAL(he); |
320 | if(flags & MOUSEf_ATTR_HAS_TC){ |
321 | value = mouse_xa_apply_type_constraint(aTHX_ xa, value, flags); |
322 | } |
ca8e67d6 |
323 | value = set_slot(object, slot, value); |
4e7e3250 |
324 | if(SvROK(value) && flags & MOUSEf_ATTR_IS_WEAK_REF){ |
325 | weaken_slot(object, slot); |
326 | } |
327 | if(flags & MOUSEf_ATTR_HAS_TRIGGER && triggers_queue){ |
328 | AV* const pair = newAV(); |
329 | av_push(pair, newSVsv( mcall0s(attr, "trigger") )); |
330 | av_push(pair, newSVsv(value)); |
331 | |
332 | av_push(triggers_queue, (SV*)pair); |
333 | } |
e128626c |
334 | used++; |
4e7e3250 |
335 | } |
336 | else { /* no init arg */ |
337 | if(flags & (MOUSEf_ATTR_HAS_DEFAULT | MOUSEf_ATTR_HAS_BUILDER)){ |
338 | if(!(flags & MOUSEf_ATTR_IS_LAZY)){ |
339 | mouse_xa_set_default(aTHX_ xa, object); |
340 | } |
341 | } |
342 | else if(flags & MOUSEf_ATTR_IS_REQUIRED) { |
343 | mouse_throw_error(attr, NULL, "Attribute (%"SVf") is required", slot); |
344 | } |
345 | } |
e128626c |
346 | } /* for each attribute */ |
347 | |
348 | if(MOUSE_xc_flags(xc) & MOUSEf_XC_CONSTRUCTOR_IS_STRICT && used < HvUSEDKEYS(args)){ |
349 | mouse_report_unknown_args(aTHX_ meta, attrs, args); |
350 | } |
4e7e3250 |
351 | |
352 | if(triggers_queue){ |
353 | len = AvFILLp(triggers_queue) + 1; |
354 | for(i = 0; i < len; i++){ |
355 | AV* const pair = (AV*)AvARRAY(triggers_queue)[i]; |
356 | SV* const trigger = AvARRAY(pair)[0]; |
357 | SV* const value = AvARRAY(pair)[1]; |
358 | |
359 | mcall1(object, trigger, value); |
360 | } |
361 | } |
362 | |
363 | if(MOUSE_xc_flags(xc) & MOUSEf_XC_IS_ANON){ |
ca8e67d6 |
364 | (void)set_slot(object, newSVpvs_flags("__METACLASS__", SVs_TEMP), meta); |
d4712779 |
365 | } |
4e7e3250 |
366 | |
aa2d2e2c |
367 | } |
368 | |
a5c683f6 |
369 | static SV* |
1bbf8369 |
370 | mouse_initialize_metaclass(pTHX_ SV* const klass) { |
371 | SV* meta = get_metaclass(klass); |
372 | |
373 | if(!SvOK(meta)){ |
374 | dSP; |
375 | PUSHMARK(SP); |
376 | |
377 | EXTEND(SP, 2); |
378 | mPUSHp("Mouse::Meta::Class", sizeof("Mouse::Meta::Class")-1); |
379 | PUSHs(klass); |
380 | PUTBACK; |
381 | |
382 | call_method("initialize", G_SCALAR); |
383 | SPAGAIN; |
384 | meta = POPs; |
385 | PUTBACK; |
386 | } |
387 | |
388 | return meta; |
389 | } |
390 | |
adb5eb76 |
391 | static void |
392 | mouse_buildall(pTHX_ AV* const xc, SV* const object, SV* const args) { |
393 | AV* const buildall = MOUSE_xc_buildall(xc); |
394 | I32 const len = AvFILLp(buildall) + 1; |
395 | I32 i; |
396 | for(i = 0; i < len; i++){ |
397 | dSP; |
398 | |
399 | PUSHMARK(SP); |
400 | EXTEND(SP, 2); |
401 | PUSHs(object); |
402 | PUSHs(args); |
403 | PUTBACK; |
404 | |
accc9fc2 |
405 | call_sv(AvARRAY(buildall)[i], G_VOID); |
406 | |
407 | /* discard a scalar which G_VOID returns */ |
408 | SPAGAIN; |
409 | (void)POPs; |
410 | PUTBACK; |
adb5eb76 |
411 | } |
412 | } |
413 | |
646c0371 |
414 | MODULE = Mouse PACKAGE = Mouse |
df6dd016 |
415 | |
416 | PROTOTYPES: DISABLE |
417 | |
cccb83de |
418 | BOOT: |
419 | mouse_package = newSVpvs_share("package"); |
420 | mouse_namespace = newSVpvs_share("namespace"); |
3e44140b |
421 | mouse_methods = newSVpvs_share("methods"); |
a5df48e5 |
422 | mouse_name = newSVpvs_share("name"); |
d39ac61c |
423 | mouse_coerce = newSVpvs_share("coerce"); |
3e44140b |
424 | |
047d7af0 |
425 | mouse_get_attribute = newSVpvs_share("get_attribute"); |
426 | mouse_get_attribute_list = newSVpvs_share("get_attribute_list"); |
da4432f3 |
427 | |
646c0371 |
428 | MOUSE_CALL_BOOT(Mouse__Util); |
1d5ecd5f |
429 | MOUSE_CALL_BOOT(Mouse__Util__TypeConstraints); |
646c0371 |
430 | MOUSE_CALL_BOOT(Mouse__Meta__Method__Accessor__XS); |
aa2d2e2c |
431 | MOUSE_CALL_BOOT(Mouse__Meta__Attribute); |
f48920c1 |
432 | |
cccb83de |
433 | |
7d96ae4d |
434 | MODULE = Mouse PACKAGE = Mouse::Meta::Module |
435 | |
436 | BOOT: |
437 | INSTALL_SIMPLE_READER_WITH_KEY(Module, name, package); |
438 | INSTALL_SIMPLE_READER_WITH_KEY(Module, _method_map, methods); |
439 | INSTALL_SIMPLE_READER_WITH_KEY(Module, _attribute_map, attributes); |
440 | |
441 | HV* |
442 | namespace(SV* self) |
443 | CODE: |
444 | { |
6a97bbda |
445 | SV* const package = get_slot(self, mouse_package); |
7d96ae4d |
446 | if(!(package && SvOK(package))){ |
3e44140b |
447 | croak("No package name defined"); |
7d96ae4d |
448 | } |
449 | RETVAL = gv_stashsv(package, GV_ADDMULTI); |
450 | } |
451 | OUTPUT: |
452 | RETVAL |
453 | |
3e44140b |
454 | # ignore extra arguments for extensibility |
455 | void |
456 | add_method(SV* self, SV* name, SV* code, ...) |
457 | CODE: |
458 | { |
6a97bbda |
459 | SV* const package = get_slot(self, mouse_package); /* $self->{package} */ |
460 | SV* const methods = get_slot(self, mouse_methods); /* $self->{methods} */ |
3e44140b |
461 | GV* gv; |
462 | SV* code_ref; |
463 | |
464 | if(!(package && SvOK(package))){ |
465 | croak("No package name defined"); |
466 | } |
467 | |
468 | SvGETMAGIC(name); |
469 | SvGETMAGIC(code); |
470 | |
471 | if(!SvOK(name)){ |
472 | mouse_throw_error(self, NULL, "You must define a method name"); |
473 | } |
474 | if(!SvROK(code)){ |
475 | mouse_throw_error(self, NULL, "You must define a CODE reference"); |
476 | } |
477 | |
478 | code_ref = code; |
479 | if(SvTYPE(SvRV(code_ref)) != SVt_PVCV){ |
480 | SV* sv = code_ref; /* used in tryAMAGICunDEREF */ |
481 | SV** sp = &sv; /* used in tryAMAGICunDEREF */ |
482 | tryAMAGICunDEREF(to_cv); /* try \&{$code} */ |
24d5564d |
483 | if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)){ |
484 | mouse_throw_error(self, NULL, "You must pass a CODE reference to add_method"); |
3e44140b |
485 | } |
486 | code_ref = sv; |
487 | } |
488 | |
489 | /* *{$package . '::' . $name} -> *gv */ |
490 | gv = gv_fetchpv(form("%"SVf"::%"SVf, package, name), GV_ADDMULTI, SVt_PVCV); |
491 | if(GvCVu(gv)){ /* delete *slot{gv} to work around "redefine" warning */ |
492 | SvREFCNT_dec(GvCV(gv)); |
493 | GvCV(gv) = NULL; |
494 | } |
495 | sv_setsv_mg((SV*)gv, code_ref); /* *gv = $code_ref */ |
496 | |
ca8e67d6 |
497 | (void)set_slot(methods, name, code); /* $self->{methods}{$name} = $code */ |
3e44140b |
498 | |
855eff0e |
499 | /* name the CODE ref if it's anonymous */ |
500 | { |
501 | CV* const code_entity = (CV*)SvRV(code_ref); |
502 | if(CvANON(code_entity) |
503 | && CvGV(code_entity) /* a cv under construction has no gv */ ){ |
40e57f0a |
504 | HV* dbsub; |
505 | |
506 | /* update %DB::sub to make NYTProf happy */ |
507 | if((PL_perldb & (PERLDBf_SUBLINE|PERLDB_NAMEANON)) |
508 | && PL_DBsub && (dbsub = GvHV(PL_DBsub)) |
509 | ){ |
510 | /* see Perl_newATTRSUB() in op.c */ |
511 | SV* const subname = sv_newmortal(); |
512 | HE* orig; |
513 | |
514 | gv_efullname3(subname, CvGV(code_entity), NULL); |
515 | orig = hv_fetch_ent(dbsub, subname, FALSE, 0U); |
516 | if(orig){ |
517 | gv_efullname3(subname, gv, NULL); |
518 | (void)hv_store_ent(dbsub, subname, HeVAL(orig), 0U); |
519 | SvREFCNT_inc_simple_void_NN(HeVAL(orig)); |
520 | } |
521 | } |
855eff0e |
522 | |
523 | CvGV(code_entity) = gv; |
524 | CvANON_off(code_entity); |
525 | } |
526 | } |
3e44140b |
527 | } |
528 | |
43165725 |
529 | MODULE = Mouse PACKAGE = Mouse::Meta::Class |
530 | |
531 | BOOT: |
532 | INSTALL_SIMPLE_READER(Class, roles); |
533 | INSTALL_SIMPLE_PREDICATE_WITH_KEY(Class, is_anon_class, anon_serial_id); |
80efe911 |
534 | INSTALL_SIMPLE_READER(Class, is_immutable); |
e128626c |
535 | INSTALL_SIMPLE_READER_WITH_KEY(Class, __strict_constructor, strict_constructor); |
a5c683f6 |
536 | |
787f84c2 |
537 | INSTALL_CLASS_HOLDER(Class, method_metaclass, "Mouse::Meta::Method"); |
538 | INSTALL_CLASS_HOLDER(Class, attribute_metaclass, "Mouse::Meta::Attribute"); |
539 | INSTALL_CLASS_HOLDER(Class, constructor_class, "Mouse::Meta::Method::Constructor::XS"); |
540 | INSTALL_CLASS_HOLDER(Class, destructor_class, "Mouse::Meta::Method::Destructor::XS"); |
a5c683f6 |
541 | |
542 | newCONSTSUB(gv_stashpvs("Mouse::Meta::Method::Constructor::XS", TRUE), "_generate_constructor", |
543 | newRV_inc((SV*)get_cvs("Mouse::Object::new", TRUE))); |
544 | newCONSTSUB(gv_stashpvs("Mouse::Meta::Method::Destructor::XS", TRUE), "_generate_destructor", |
545 | newRV_inc((SV*)get_cvs("Mouse::Object::DESTROY", TRUE))); |
546 | |
43165725 |
547 | |
cccb83de |
548 | void |
549 | linearized_isa(SV* self) |
550 | PPCODE: |
551 | { |
552 | SV* const stash_ref = mcall0(self, mouse_namespace); /* $self->namespace */ |
553 | AV* linearized_isa; |
554 | I32 len; |
555 | I32 i; |
556 | if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)){ |
557 | croak("namespace() didn't return a HASH reference"); |
558 | } |
559 | linearized_isa = mro_get_linear_isa((HV*)SvRV(stash_ref)); |
560 | len = AvFILLp(linearized_isa) + 1; |
561 | EXTEND(SP, len); |
562 | for(i = 0; i < len; i++){ |
563 | PUSHs(AvARRAY(linearized_isa)[i]); |
564 | } |
565 | } |
566 | |
da4432f3 |
567 | void |
568 | get_all_attributes(SV* self) |
569 | PPCODE: |
570 | { |
0aad0266 |
571 | AV* const xc = mouse_get_xc(aTHX_ self); |
ae34e077 |
572 | AV* const all_attrs = MOUSE_xc_attrall(xc); |
da4432f3 |
573 | I32 const len = AvFILLp(all_attrs) + 1; |
574 | I32 i; |
575 | |
576 | EXTEND(SP, len); |
577 | for(i = 0; i < len; i++){ |
578 | PUSHs( MOUSE_av_at(all_attrs, i) ); |
579 | } |
580 | } |
441964ce |
581 | |
aa2d2e2c |
582 | SV* |
ba153b33 |
583 | new_object(SV* meta, ...) |
aa2d2e2c |
584 | CODE: |
585 | { |
aa2d2e2c |
586 | AV* const xc = mouse_get_xc(aTHX_ meta); |
074a414d |
587 | HV* const args = mouse_buildargs(aTHX_ meta, NULL, ax, items); |
d4712779 |
588 | |
aa2d2e2c |
589 | RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); |
d4712779 |
590 | mouse_class_initialize_object(aTHX_ meta, RETVAL, args, FALSE); |
aa2d2e2c |
591 | } |
ba153b33 |
592 | OUTPUT: |
593 | RETVAL |
aa2d2e2c |
594 | |
595 | void |
4e7e3250 |
596 | _initialize_object(SV* meta, SV* object, HV* args, bool ignore_triggers = FALSE) |
aa2d2e2c |
597 | CODE: |
598 | { |
d4712779 |
599 | mouse_class_initialize_object(aTHX_ meta, object, args, ignore_triggers); |
aa2d2e2c |
600 | } |
601 | |
43165725 |
602 | MODULE = Mouse PACKAGE = Mouse::Meta::Role |
603 | |
604 | BOOT: |
605 | INSTALL_SIMPLE_READER_WITH_KEY(Role, get_roles, roles); |
606 | INSTALL_SIMPLE_PREDICATE_WITH_KEY(Role, is_anon_role, anon_serial_id); |
607 | |
787f84c2 |
608 | INSTALL_CLASS_HOLDER(Role, method_metaclass, "Mouse::Meta::Role::Method"); |
e058b279 |
609 | |
aa2d2e2c |
610 | MODULE = Mouse PACKAGE = Mouse::Object |
43165725 |
611 | |
074a414d |
612 | SV* |
613 | new(SV* klass, ...) |
614 | CODE: |
615 | { |
1bbf8369 |
616 | SV* const meta = mouse_initialize_metaclass(aTHX_ klass); |
074a414d |
617 | AV* const xc = mouse_get_xc(aTHX_ meta); |
618 | UV const flags = MOUSE_xc_flags(xc); |
619 | SV* args; |
074a414d |
620 | |
621 | /* BUILDARGS */ |
622 | if(flags & MOUSEf_XC_HAS_BUILDARGS){ |
adb5eb76 |
623 | I32 i; |
074a414d |
624 | SPAGAIN; |
625 | |
626 | PUSHMARK(SP); |
627 | EXTEND(SP, items); |
628 | for(i = 0; i < items; i++){ |
629 | PUSHs(ST(i)); |
630 | } |
631 | //SP += items; |
632 | PUTBACK; |
633 | call_method("BUILDARGS", G_SCALAR); |
634 | SPAGAIN; |
635 | args = POPs; |
636 | PUTBACK; |
637 | |
638 | if(!IsHashRef(args)){ |
639 | croak("BUILDARGS did not return a HASH reference"); |
640 | } |
641 | } |
642 | else{ |
643 | args = newRV_inc((SV*)mouse_buildargs(aTHX_ meta, klass, ax, items)); |
644 | sv_2mortal(args); |
645 | } |
646 | |
647 | /* new_object */ |
648 | RETVAL = mouse_instance_create(aTHX_ MOUSE_xc_stash(xc)); |
649 | mouse_class_initialize_object(aTHX_ meta, RETVAL, (HV*)SvRV(args), FALSE); |
650 | |
adb5eb76 |
651 | mouse_buildall(aTHX_ xc, RETVAL, args); |
074a414d |
652 | } |
653 | OUTPUT: |
654 | RETVAL |
655 | |
a5c683f6 |
656 | void |
657 | DESTROY(SV* object) |
adb5eb76 |
658 | ALIAS: |
659 | DESTROY = 0 |
660 | DEMOLISHALL = 1 |
aa2d2e2c |
661 | CODE: |
662 | { |
a5c683f6 |
663 | SV* const meta = get_metaclass(object); |
664 | AV* demolishall; |
665 | I32 len, i; |
666 | |
667 | if(!IsObject(object)){ |
31113a4a |
668 | croak("You must not call %s as a class method", |
669 | ix == 0 ? "DESTROY" : "DEMOLISHALL"); |
a5c683f6 |
670 | } |
671 | |
672 | if(SvOK(meta)){ |
673 | AV* const xc = mouse_get_xc(aTHX_ meta); |
674 | |
675 | demolishall = MOUSE_xc_demolishall(xc); |
676 | } |
adb5eb76 |
677 | else { /* The metaclass is already destroyed */ |
a5c683f6 |
678 | AV* const linearized_isa = mro_get_linear_isa(SvSTASH(SvRV(object))); |
7efbc77d |
679 | |
a5c683f6 |
680 | len = AvFILLp(linearized_isa) + 1; |
7efbc77d |
681 | |
a5c683f6 |
682 | demolishall = newAV_mortal(); |
683 | for(i = 0; i < len; i++){ |
684 | SV* const klass = MOUSE_av_at(linearized_isa, i); |
685 | HV* const st = gv_stashsv(klass, TRUE); |
686 | GV* const gv = stash_fetchs(st, "DEMOLISH", FALSE); |
687 | if(gv && GvCVu(gv)){ |
688 | av_push(demolishall, newRV_inc((SV*)GvCV(gv))); |
689 | } |
690 | } |
691 | } |
692 | |
a5c683f6 |
693 | len = AvFILLp(demolishall) + 1; |
694 | if(len > 0){ |
695 | GV* const statusvalue = gv_fetchpvs("?", 0, SVt_PV); |
70425827 |
696 | |
697 | if(statusvalue){ /* it can be NULL */ |
698 | SAVESPTR(GvSV(statusvalue)); /* local $? */ |
699 | GvSV(statusvalue) = sv_newmortal(); |
700 | } |
a5c683f6 |
701 | SAVESPTR(ERRSV); /* local $@ */ |
70425827 |
702 | ERRSV = newSVpvs_flags("", SVs_TEMP); |
a5c683f6 |
703 | |
a5c683f6 |
704 | for(i = 0; i < len; i++){ |
f8beb873 |
705 | SPAGAIN; |
a5c683f6 |
706 | |
707 | PUSHMARK(SP); |
708 | XPUSHs(object); |
6514735e |
709 | XPUSHs(boolSV(PL_dirty)); |
a5c683f6 |
710 | PUTBACK; |
711 | |
accc9fc2 |
712 | call_sv(AvARRAY(demolishall)[i], G_VOID | G_EVAL); |
713 | |
714 | /* discard a scalar which G_VOID returns */ |
715 | SPAGAIN; |
716 | (void)POPs; |
717 | PUTBACK; |
718 | |
6ad77996 |
719 | if(sv_true(ERRSV)){ |
a5c683f6 |
720 | SV* const e = newSVsv(ERRSV); |
721 | |
722 | FREETMPS; |
723 | LEAVE; |
724 | |
725 | sv_setsv(ERRSV, e); |
726 | SvREFCNT_dec(e); |
727 | croak(NULL); /* rethrow */ |
728 | } |
729 | } |
730 | } |
731 | } |
732 | |
733 | HV* |
734 | BUILDARGS(SV* klass, ...) |
7efbc77d |
735 | CODE: |
736 | { |
a5c683f6 |
737 | RETVAL = mouse_buildargs(aTHX_ NULL, klass, ax, items); |
7efbc77d |
738 | } |
739 | OUTPUT: |
740 | RETVAL |
741 | |
a5c683f6 |
742 | |
adb5eb76 |
743 | void |
744 | BUILDALL(SV* self, SV* args) |
745 | CODE: |
746 | { |
f50e50d6 |
747 | SV* const meta = get_metaclass(self); |
748 | AV* const xc = mouse_get_xc(aTHX_ meta); |
adb5eb76 |
749 | |
750 | if(!IsHashRef(args)){ |
751 | croak("You must pass a HASH reference to BUILDALL"); |
752 | } |
adb5eb76 |
753 | mouse_buildall(aTHX_ xc, self, args); |
754 | } |