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