Commit | Line | Data |
646c0371 |
1 | #include "mouse.h" |
2 | |
73d8d597 |
3 | #define CHECK_INSTANCE(instance) STMT_START{ \ |
fcdc12ac |
4 | assert(instance); \ |
73d8d597 |
5 | if(!(SvROK(instance) && SvTYPE(SvRV(instance)) == SVt_PVHV)){ \ |
ef53a04b |
6 | croak("Invalid object instance: '%"SVf"'", instance); \ |
73d8d597 |
7 | } \ |
646c0371 |
8 | } STMT_END |
9 | |
646c0371 |
10 | |
11 | #define MOUSE_mg_attribute(mg) MOUSE_xa_attribute(MOUSE_mg_xa(mg)) |
12 | |
646c0371 |
13 | static MGVTBL mouse_accessor_vtbl; /* MAGIC identity */ |
14 | |
cc7cd81f |
15 | #define dMOUSE_self SV* const self = mouse_accessor_get_self(aTHX_ ax, items, cv) |
646c0371 |
16 | |
fc650569 |
17 | STATIC_INLINE SV* |
646c0371 |
18 | mouse_accessor_get_self(pTHX_ I32 const ax, I32 const items, CV* const cv) { |
646c0371 |
19 | if(items < 1){ |
20 | croak("Too few arguments for %s", GvNAME(CvGV(cv))); |
21 | } |
22 | |
23 | /* NOTE: If self has GETMAGIC, $self->accessor will invoke GETMAGIC |
24 | * before calling methods, so SvGETMAGIC(self) is not necessarily needed here. |
25 | */ |
26 | |
76770976 |
27 | return ST(0); |
646c0371 |
28 | } |
29 | |
30 | |
31 | CV* |
a17f6313 |
32 | mouse_accessor_generate(pTHX_ SV* const attr, XSUBADDR_t const accessor_impl){ |
0aad0266 |
33 | AV* const xa = mouse_get_xa(aTHX_ attr); |
646c0371 |
34 | CV* xsub; |
35 | MAGIC* mg; |
646c0371 |
36 | |
37 | xsub = newXS(NULL, accessor_impl, __FILE__); |
38 | sv_2mortal((SV*)xsub); |
39 | |
0aad0266 |
40 | mg = sv_magicext((SV*)xsub, MOUSE_xa_slot(xa), PERL_MAGIC_ext, &mouse_accessor_vtbl, (char*)xa, HEf_SVKEY); |
41 | |
4e7e3250 |
42 | MOUSE_mg_flags(mg) = (U16)MOUSE_xa_flags(xa); |
646c0371 |
43 | |
44 | /* NOTE: |
45 | * although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx) |
46 | */ |
47 | CvXSUBANY(xsub).any_ptr = (void*)mg; |
48 | |
646c0371 |
49 | return xsub; |
50 | } |
51 | |
646c0371 |
52 | |
208ffaeb |
53 | #define PUSH_VALUE(value, flags) STMT_START { \ |
54 | if((flags) & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY){ \ |
55 | mouse_push_values(aTHX_ value, (flags)); \ |
56 | } \ |
57 | else{ \ |
58 | dSP; \ |
59 | XPUSHs(value ? value : &PL_sv_undef); \ |
60 | PUTBACK; \ |
61 | } \ |
62 | } STMT_END \ |
646c0371 |
63 | |
64 | /* pushes return values, does auto-deref if needed */ |
65 | static void |
66 | mouse_push_values(pTHX_ SV* const value, U16 const flags){ |
67 | dSP; |
68 | |
208ffaeb |
69 | assert( flags & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY ); |
646c0371 |
70 | |
208ffaeb |
71 | if(!(value && SvOK(value))){ |
72 | return; |
73 | } |
646c0371 |
74 | |
208ffaeb |
75 | if(flags & MOUSEf_TC_IS_ARRAYREF){ |
80aa5731 |
76 | AV* av; |
208ffaeb |
77 | I32 len; |
78 | I32 i; |
646c0371 |
79 | |
80aa5731 |
80 | if(!IsArrayRef(value)){ |
208ffaeb |
81 | croak("Mouse-panic: Not an ARRAY reference"); |
646c0371 |
82 | } |
646c0371 |
83 | |
80aa5731 |
84 | av = (AV*)SvRV(value); |
208ffaeb |
85 | len = av_len(av) + 1; |
86 | EXTEND(SP, len); |
87 | for(i = 0; i < len; i++){ |
88 | SV** const svp = av_fetch(av, i, FALSE); |
89 | PUSHs(svp ? *svp : &PL_sv_undef); |
646c0371 |
90 | } |
91 | } |
caf77af6 |
92 | else{ |
80aa5731 |
93 | HV* hv; |
208ffaeb |
94 | HE* he; |
95 | |
caf77af6 |
96 | assert(flags & MOUSEf_TC_IS_HASHREF); |
97 | |
80aa5731 |
98 | if(!IsHashRef(value)){ |
208ffaeb |
99 | croak("Mouse-panic: Not a HASH reference"); |
100 | } |
101 | |
80aa5731 |
102 | hv = (HV*)SvRV(value); |
208ffaeb |
103 | hv_iterinit(hv); |
104 | while((he = hv_iternext(hv))){ |
105 | EXTEND(SP, 2); |
106 | PUSHs(hv_iterkeysv(he)); |
107 | PUSHs(hv_iterval(hv, he)); |
108 | } |
646c0371 |
109 | } |
110 | |
111 | PUTBACK; |
112 | } |
113 | |
114 | static void |
115 | mouse_attr_get(pTHX_ SV* const self, MAGIC* const mg){ |
116 | U16 const flags = MOUSE_mg_flags(mg); |
646c0371 |
117 | SV* value; |
118 | |
4e7e3250 |
119 | value = get_slot(self, MOUSE_mg_slot(mg)); |
646c0371 |
120 | |
121 | /* check_lazy */ |
122 | if( !value && flags & MOUSEf_ATTR_IS_LAZY ){ |
4e7e3250 |
123 | value = mouse_xa_set_default(aTHX_ MOUSE_mg_xa(mg), self); |
646c0371 |
124 | } |
125 | |
208ffaeb |
126 | PUSH_VALUE(value, flags); |
646c0371 |
127 | } |
128 | |
129 | static void |
130 | mouse_attr_set(pTHX_ SV* const self, MAGIC* const mg, SV* value){ |
131 | U16 const flags = MOUSE_mg_flags(mg); |
132 | SV* const slot = MOUSE_mg_slot(mg); |
133 | |
134 | if(flags & MOUSEf_ATTR_HAS_TC){ |
4e7e3250 |
135 | value = mouse_xa_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags); |
646c0371 |
136 | } |
137 | |
ca8e67d6 |
138 | value = set_slot(self, slot, value); |
646c0371 |
139 | |
140 | if(flags & MOUSEf_ATTR_IS_WEAK_REF){ |
6fe2272b |
141 | weaken_slot(self, slot); |
646c0371 |
142 | } |
143 | |
144 | if(flags & MOUSEf_ATTR_HAS_TRIGGER){ |
145 | SV* const trigger = mcall0s(MOUSE_mg_attribute(mg), "trigger"); |
146 | dSP; |
147 | |
8ab2c6ab |
148 | /* NOTE: triggers can remove value, so |
149 | value must be copied here, |
150 | revealed by Net::Google::DataAPI (DANJOU). |
151 | */ |
152 | value = sv_mortalcopy(value); |
153 | |
646c0371 |
154 | PUSHMARK(SP); |
155 | EXTEND(SP, 2); |
156 | PUSHs(self); |
157 | PUSHs(value); |
158 | |
159 | PUTBACK; |
b3cd4c14 |
160 | call_sv_safe(trigger, G_VOID | G_DISCARD); |
646c0371 |
161 | /* need not SPAGAIN */ |
8ab2c6ab |
162 | |
163 | assert(SvTYPE(value) != SVTYPEMASK); |
646c0371 |
164 | } |
165 | |
208ffaeb |
166 | PUSH_VALUE(value, flags); |
646c0371 |
167 | } |
168 | |
30e11004 |
169 | XS(XS_Mouse_accessor) |
646c0371 |
170 | { |
171 | dVAR; dXSARGS; |
172 | dMOUSE_self; |
173 | MAGIC* const mg = (MAGIC*)XSANY.any_ptr; |
174 | |
175 | SP -= items; /* PPCODE */ |
176 | PUTBACK; |
177 | |
178 | if(items == 1){ /* reader */ |
179 | mouse_attr_get(aTHX_ self, mg); |
180 | } |
181 | else if (items == 2){ /* writer */ |
182 | mouse_attr_set(aTHX_ self, mg, ST(1)); |
183 | } |
184 | else{ |
185 | mouse_throw_error(MOUSE_mg_attribute(mg), NULL, |
fad99075 |
186 | "Expected exactly one or two argument for an accessor of %"SVf, |
187 | MOUSE_mg_slot(mg)); |
646c0371 |
188 | } |
189 | } |
190 | |
191 | |
30e11004 |
192 | XS(XS_Mouse_reader) |
646c0371 |
193 | { |
194 | dVAR; dXSARGS; |
195 | dMOUSE_self; |
196 | MAGIC* const mg = (MAGIC*)XSANY.any_ptr; |
197 | |
198 | if (items != 1) { |
199 | mouse_throw_error(MOUSE_mg_attribute(mg), NULL, |
fad99075 |
200 | "Cannot assign a value to a read-only accessor of %"SVf, |
201 | MOUSE_mg_slot(mg)); |
646c0371 |
202 | } |
203 | |
204 | SP -= items; /* PPCODE */ |
205 | PUTBACK; |
206 | |
207 | mouse_attr_get(aTHX_ self, mg); |
208 | } |
209 | |
30e11004 |
210 | XS(XS_Mouse_writer) |
646c0371 |
211 | { |
212 | dVAR; dXSARGS; |
213 | dMOUSE_self; |
214 | MAGIC* const mg = (MAGIC*)XSANY.any_ptr; |
215 | |
216 | if (items != 2) { |
217 | mouse_throw_error(MOUSE_mg_attribute(mg), NULL, |
fad99075 |
218 | "Too few arguments for a write-only accessor of %"SVf, |
219 | MOUSE_mg_slot(mg)); |
646c0371 |
220 | } |
221 | |
222 | SP -= items; /* PPCODE */ |
223 | PUTBACK; |
224 | |
225 | mouse_attr_set(aTHX_ self, mg, ST(1)); |
226 | } |
227 | |
228 | /* simple accessors */ |
229 | |
230 | /* |
231 | static MAGIC* |
232 | mouse_accessor_get_mg(pTHX_ CV* const xsub){ |
233 | return moose_mg_find(aTHX_ (SV*)xsub, &mouse_simple_accessor_vtbl, MOOSEf_DIE_ON_FAIL); |
234 | } |
235 | */ |
236 | |
237 | CV* |
fad99075 |
238 | mouse_simple_accessor_generate(pTHX_ |
239 | const char* const fq_name, const char* const key, I32 const keylen, |
240 | XSUBADDR_t const accessor_impl, void* const dptr, I32 const dlen) { |
646c0371 |
241 | CV* const xsub = newXS((char*)fq_name, accessor_impl, __FILE__); |
242 | SV* const slot = newSVpvn_share(key, keylen, 0U); |
243 | MAGIC* mg; |
244 | |
245 | if(!fq_name){ |
246 | /* anonymous xsubs need sv_2mortal */ |
247 | sv_2mortal((SV*)xsub); |
248 | } |
249 | |
e058b279 |
250 | mg = sv_magicext((SV*)xsub, slot, PERL_MAGIC_ext, &mouse_accessor_vtbl, (char*)dptr, dlen); |
646c0371 |
251 | SvREFCNT_dec(slot); /* sv_magicext() increases refcnt in mg_obj */ |
e058b279 |
252 | if(dlen == HEf_SVKEY){ |
253 | SvREFCNT_dec(dptr); |
254 | } |
646c0371 |
255 | |
256 | /* NOTE: |
257 | * although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx) |
258 | */ |
259 | CvXSUBANY(xsub).any_ptr = (void*)mg; |
260 | |
261 | return xsub; |
262 | } |
263 | |
30e11004 |
264 | XS(XS_Mouse_simple_reader) |
646c0371 |
265 | { |
266 | dVAR; dXSARGS; |
267 | dMOUSE_self; |
e058b279 |
268 | MAGIC* const mg = (MAGIC*)XSANY.any_ptr; |
646c0371 |
269 | SV* value; |
270 | |
271 | if (items != 1) { |
fad99075 |
272 | croak("Expected exactly one argument for a reader of %"SVf, |
273 | MOUSE_mg_slot(mg)); |
646c0371 |
274 | } |
275 | |
e058b279 |
276 | value = get_slot(self, MOUSE_mg_slot(mg)); |
2468f1d7 |
277 | if(!value) { |
278 | if(MOUSE_mg_ptr(mg)){ |
279 | /* the default value must be a SV */ |
280 | assert(MOUSE_mg_len(mg) == HEf_SVKEY); |
281 | value = (SV*)MOUSE_mg_ptr(mg); |
282 | } |
283 | else{ |
284 | value = &PL_sv_undef; |
285 | } |
286 | } |
287 | |
288 | ST(0) = value; |
646c0371 |
289 | XSRETURN(1); |
290 | } |
291 | |
292 | |
30e11004 |
293 | XS(XS_Mouse_simple_writer) |
646c0371 |
294 | { |
295 | dVAR; dXSARGS; |
296 | dMOUSE_self; |
297 | SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr); |
298 | |
299 | if (items != 2) { |
fad99075 |
300 | croak("Expected exactly two argument for a writer of %"SVf, |
301 | slot); |
646c0371 |
302 | } |
303 | |
6fe2272b |
304 | ST(0) = set_slot(self, slot, ST(1)); |
646c0371 |
305 | XSRETURN(1); |
306 | } |
307 | |
30e11004 |
308 | XS(XS_Mouse_simple_clearer) |
646c0371 |
309 | { |
310 | dVAR; dXSARGS; |
311 | dMOUSE_self; |
312 | SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr); |
313 | SV* value; |
314 | |
315 | if (items != 1) { |
fad99075 |
316 | croak("Expected exactly one argument for a clearer of %"SVf, |
317 | slot); |
646c0371 |
318 | } |
319 | |
6fe2272b |
320 | value = delete_slot(self, slot); |
646c0371 |
321 | ST(0) = value ? value : &PL_sv_undef; |
322 | XSRETURN(1); |
323 | } |
324 | |
30e11004 |
325 | XS(XS_Mouse_simple_predicate) |
646c0371 |
326 | { |
327 | dVAR; dXSARGS; |
328 | dMOUSE_self; |
329 | SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr); |
330 | |
331 | if (items != 1) { |
fad99075 |
332 | croak("Expected exactly one argument for a predicate of %"SVf, slot); |
646c0371 |
333 | } |
334 | |
6fe2272b |
335 | ST(0) = boolSV( has_slot(self, slot) ); |
646c0371 |
336 | XSRETURN(1); |
337 | } |
338 | |
fad99075 |
339 | /* Class::Data::Inheritable-like class accessor */ |
340 | XS(XS_Mouse_inheritable_class_accessor) { |
341 | dVAR; dXSARGS; |
342 | dMOUSE_self; |
343 | SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr); |
344 | SV* value; |
345 | SV* stash_ref; |
346 | HV* stash; |
347 | |
348 | if(items == 1){ /* reader */ |
349 | value = NULL; |
350 | } |
351 | else if (items == 2){ /* writer */ |
352 | value = ST(1); |
353 | } |
354 | else{ |
355 | croak("Expected exactly one or two argument for a class data accessor" |
356 | "of %"SVf, slot); |
357 | value = NULL; /* -Wuninitialized */ |
358 | } |
359 | |
360 | stash_ref= mcall0(self, mouse_namespace); |
361 | if(!(SvROK(stash_ref) && SvTYPE(SvRV(stash_ref)) == SVt_PVHV)) { |
362 | croak("namespace() didn't return a HASH reference"); |
363 | } |
364 | stash = (HV*)SvRV(stash_ref); |
365 | |
366 | if(!value) { /* reader */ |
367 | value = get_slot(self, slot); |
368 | if(!value) { |
369 | AV* const isa = mro_get_linear_isa(stash); |
370 | I32 const len = av_len(isa) + 1; |
371 | I32 i; |
372 | for(i = 1; i < len; i++) { |
373 | SV* const klass = MOUSE_av_at(isa, i); |
374 | SV* const meta = get_metaclass(klass); |
375 | if(!SvOK(meta)){ |
376 | continue; /* skip non-Mouse classes */ |
377 | } |
378 | value = get_slot(meta, slot); |
379 | if(value) { |
380 | break; |
381 | } |
382 | } |
383 | if(!value) { |
384 | value = &PL_sv_undef; |
385 | } |
386 | } |
387 | } |
388 | else { /* writer */ |
389 | set_slot(self, slot, value); |
390 | mro_method_changed_in(stash); |
391 | } |
392 | |
393 | ST(0) = value; |
394 | XSRETURN(1); |
395 | } |
396 | |
76770976 |
397 | /* simple instance slot accessor (or Mouse::Meta::Instance) */ |
646c0371 |
398 | |
399 | SV* |
400 | mouse_instance_create(pTHX_ HV* const stash) { |
fcdc12ac |
401 | SV* instance; |
646c0371 |
402 | assert(stash); |
aa2d2e2c |
403 | assert(SvTYPE(stash) == SVt_PVHV); |
fcdc12ac |
404 | instance = sv_bless( newRV_noinc((SV*)newHV()), stash ); |
405 | return sv_2mortal(instance); |
646c0371 |
406 | } |
407 | |
408 | SV* |
409 | mouse_instance_clone(pTHX_ SV* const instance) { |
fcdc12ac |
410 | SV* proto; |
646c0371 |
411 | CHECK_INSTANCE(instance); |
fcdc12ac |
412 | assert(SvOBJECT(SvRV(instance))); |
413 | |
414 | proto = newRV_noinc((SV*)newHVhv((HV*)SvRV(instance))); |
415 | sv_bless(proto, SvSTASH(SvRV(instance))); |
416 | return sv_2mortal(proto); |
646c0371 |
417 | } |
418 | |
419 | bool |
420 | mouse_instance_has_slot(pTHX_ SV* const instance, SV* const slot) { |
646c0371 |
421 | assert(slot); |
422 | CHECK_INSTANCE(instance); |
423 | return hv_exists_ent((HV*)SvRV(instance), slot, 0U); |
424 | } |
425 | |
426 | SV* |
427 | mouse_instance_get_slot(pTHX_ SV* const instance, SV* const slot) { |
428 | HE* he; |
646c0371 |
429 | assert(slot); |
430 | CHECK_INSTANCE(instance); |
431 | he = hv_fetch_ent((HV*)SvRV(instance), slot, FALSE, 0U); |
432 | return he ? HeVAL(he) : NULL; |
433 | } |
434 | |
435 | SV* |
436 | mouse_instance_set_slot(pTHX_ SV* const instance, SV* const slot, SV* const value) { |
437 | HE* he; |
438 | SV* sv; |
646c0371 |
439 | assert(slot); |
440 | assert(value); |
441 | CHECK_INSTANCE(instance); |
442 | he = hv_fetch_ent((HV*)SvRV(instance), slot, TRUE, 0U); |
443 | sv = HeVAL(he); |
957474ad |
444 | sv_setsv(sv, value); |
445 | SvSETMAGIC(sv); |
646c0371 |
446 | return sv; |
447 | } |
448 | |
449 | SV* |
450 | mouse_instance_delete_slot(pTHX_ SV* const instance, SV* const slot) { |
451 | assert(instance); |
452 | assert(slot); |
453 | CHECK_INSTANCE(instance); |
454 | return hv_delete_ent((HV*)SvRV(instance), slot, 0, 0U); |
455 | } |
456 | |
457 | void |
458 | mouse_instance_weaken_slot(pTHX_ SV* const instance, SV* const slot) { |
459 | HE* he; |
646c0371 |
460 | assert(slot); |
461 | CHECK_INSTANCE(instance); |
462 | he = hv_fetch_ent((HV*)SvRV(instance), slot, FALSE, 0U); |
463 | if(he){ |
464 | sv_rvweaken(HeVAL(he)); |
465 | } |
466 | } |
346a3ab8 |
467 | |
646c0371 |
468 | MODULE = Mouse::Meta::Method::Accessor::XS PACKAGE = Mouse::Meta::Method::Accessor::XS |
469 | |
470 | PROTOTYPES: DISABLE |
471 | VERSIONCHECK: DISABLE |
472 | |
473 | CV* |
474 | _generate_accessor(klass, SV* attr, metaclass) |
475 | CODE: |
476 | { |
a17f6313 |
477 | RETVAL = mouse_accessor_generate(aTHX_ attr, XS_Mouse_accessor); |
646c0371 |
478 | } |
479 | OUTPUT: |
480 | RETVAL |
481 | |
482 | CV* |
483 | _generate_reader(klass, SV* attr, metaclass) |
484 | CODE: |
485 | { |
a17f6313 |
486 | RETVAL = mouse_accessor_generate(aTHX_ attr, XS_Mouse_reader); |
646c0371 |
487 | } |
488 | OUTPUT: |
489 | RETVAL |
490 | |
491 | CV* |
492 | _generate_writer(klass, SV* attr, metaclass) |
493 | CODE: |
494 | { |
a17f6313 |
495 | RETVAL = mouse_accessor_generate(aTHX_ attr, XS_Mouse_writer); |
646c0371 |
496 | } |
497 | OUTPUT: |
498 | RETVAL |
499 | |
500 | CV* |
501 | _generate_clearer(klass, SV* attr, metaclass) |
502 | CODE: |
503 | { |
06afbf31 |
504 | SV* const slot = mcall0(attr, mouse_name); |
646c0371 |
505 | STRLEN len; |
506 | const char* const pv = SvPV_const(slot, len); |
a17f6313 |
507 | RETVAL = mouse_simple_accessor_generate(aTHX_ NULL, pv, len, XS_Mouse_simple_clearer, NULL, 0); |
646c0371 |
508 | } |
509 | OUTPUT: |
510 | RETVAL |
511 | |
512 | CV* |
513 | _generate_predicate(klass, SV* attr, metaclass) |
514 | CODE: |
515 | { |
06afbf31 |
516 | SV* const slot = mcall0(attr, mouse_name); |
646c0371 |
517 | STRLEN len; |
518 | const char* const pv = SvPV_const(slot, len); |
a17f6313 |
519 | RETVAL = mouse_simple_accessor_generate(aTHX_ NULL, pv, len, XS_Mouse_simple_predicate, NULL, 0); |
646c0371 |
520 | } |
521 | OUTPUT: |
522 | RETVAL |
523 | |