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