Make some functions static
[gitmo/Mouse.git] / xs-src / MouseAccessor.xs
CommitLineData
646c0371 1#include "mouse.h"
2
3#define CHECK_INSTANCE(instance) STMT_START{ \
4 if(!(SvROK(instance) && SvTYPE(SvRV(instance)) == SVt_PVHV)){ \
76770976 5 croak("Invalid object instance"); \
646c0371 6 } \
7 } STMT_END
8
646c0371 9
10#define MOUSE_mg_attribute(mg) MOUSE_xa_attribute(MOUSE_mg_xa(mg))
11
646c0371 12static MGVTBL mouse_accessor_vtbl; /* MAGIC identity */
13
14
15SV*
16mouse_accessor_get_self(pTHX_ I32 const ax, I32 const items, CV* const cv) {
646c0371 17 if(items < 1){
18 croak("Too few arguments for %s", GvNAME(CvGV(cv)));
19 }
20
21 /* NOTE: If self has GETMAGIC, $self->accessor will invoke GETMAGIC
22 * before calling methods, so SvGETMAGIC(self) is not necessarily needed here.
23 */
24
76770976 25 return ST(0);
646c0371 26}
27
28
29CV*
30mouse_instantiate_xs_accessor(pTHX_ SV* const attr, XSUBADDR_t const accessor_impl){
0aad0266 31 AV* const xa = mouse_get_xa(aTHX_ attr);
646c0371 32 CV* xsub;
33 MAGIC* mg;
646c0371 34
35 xsub = newXS(NULL, accessor_impl, __FILE__);
36 sv_2mortal((SV*)xsub);
37
0aad0266 38 mg = sv_magicext((SV*)xsub, MOUSE_xa_slot(xa), PERL_MAGIC_ext, &mouse_accessor_vtbl, (char*)xa, HEf_SVKEY);
39
4e7e3250 40 MOUSE_mg_flags(mg) = (U16)MOUSE_xa_flags(xa);
646c0371 41
42 /* NOTE:
43 * although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx)
44 */
45 CvXSUBANY(xsub).any_ptr = (void*)mg;
46
646c0371 47 return xsub;
48}
49
646c0371 50
208ffaeb 51#define PUSH_VALUE(value, flags) STMT_START { \
52 if((flags) & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY){ \
53 mouse_push_values(aTHX_ value, (flags)); \
54 } \
55 else{ \
56 dSP; \
57 XPUSHs(value ? value : &PL_sv_undef); \
58 PUTBACK; \
59 } \
60 } STMT_END \
646c0371 61
62/* pushes return values, does auto-deref if needed */
63static void
64mouse_push_values(pTHX_ SV* const value, U16 const flags){
65 dSP;
66
208ffaeb 67 assert( flags & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY );
646c0371 68
208ffaeb 69 if(!(value && SvOK(value))){
70 return;
71 }
646c0371 72
208ffaeb 73 if(flags & MOUSEf_TC_IS_ARRAYREF){
80aa5731 74 AV* av;
208ffaeb 75 I32 len;
76 I32 i;
646c0371 77
80aa5731 78 if(!IsArrayRef(value)){
208ffaeb 79 croak("Mouse-panic: Not an ARRAY reference");
646c0371 80 }
646c0371 81
80aa5731 82 av = (AV*)SvRV(value);
208ffaeb 83 len = av_len(av) + 1;
84 EXTEND(SP, len);
85 for(i = 0; i < len; i++){
86 SV** const svp = av_fetch(av, i, FALSE);
87 PUSHs(svp ? *svp : &PL_sv_undef);
646c0371 88 }
89 }
208ffaeb 90 else if(flags & MOUSEf_TC_IS_HASHREF){
80aa5731 91 HV* hv;
208ffaeb 92 HE* he;
93
80aa5731 94 if(!IsHashRef(value)){
208ffaeb 95 croak("Mouse-panic: Not a HASH reference");
96 }
97
80aa5731 98 hv = (HV*)SvRV(value);
208ffaeb 99 hv_iterinit(hv);
100 while((he = hv_iternext(hv))){
101 EXTEND(SP, 2);
102 PUSHs(hv_iterkeysv(he));
103 PUSHs(hv_iterval(hv, he));
104 }
646c0371 105 }
106
107 PUTBACK;
108}
109
110static void
111mouse_attr_get(pTHX_ SV* const self, MAGIC* const mg){
112 U16 const flags = MOUSE_mg_flags(mg);
646c0371 113 SV* value;
114
4e7e3250 115 value = get_slot(self, MOUSE_mg_slot(mg));
646c0371 116
117 /* check_lazy */
118 if( !value && flags & MOUSEf_ATTR_IS_LAZY ){
4e7e3250 119 value = mouse_xa_set_default(aTHX_ MOUSE_mg_xa(mg), self);
646c0371 120 }
121
208ffaeb 122 PUSH_VALUE(value, flags);
646c0371 123}
124
125static void
126mouse_attr_set(pTHX_ SV* const self, MAGIC* const mg, SV* value){
127 U16 const flags = MOUSE_mg_flags(mg);
128 SV* const slot = MOUSE_mg_slot(mg);
129
130 if(flags & MOUSEf_ATTR_HAS_TC){
4e7e3250 131 value = mouse_xa_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags);
646c0371 132 }
133
6fe2272b 134 set_slot(self, slot, value);
646c0371 135
136 if(flags & MOUSEf_ATTR_IS_WEAK_REF){
6fe2272b 137 weaken_slot(self, slot);
646c0371 138 }
139
140 if(flags & MOUSEf_ATTR_HAS_TRIGGER){
141 SV* const trigger = mcall0s(MOUSE_mg_attribute(mg), "trigger");
142 dSP;
143
144 PUSHMARK(SP);
145 EXTEND(SP, 2);
146 PUSHs(self);
147 PUSHs(value);
148
149 PUTBACK;
150 call_sv(trigger, G_VOID | G_DISCARD);
151 /* need not SPAGAIN */
152 }
153
208ffaeb 154 PUSH_VALUE(value, flags);
646c0371 155}
156
30e11004 157XS(XS_Mouse_accessor)
646c0371 158{
159 dVAR; dXSARGS;
160 dMOUSE_self;
161 MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
162
163 SP -= items; /* PPCODE */
164 PUTBACK;
165
166 if(items == 1){ /* reader */
167 mouse_attr_get(aTHX_ self, mg);
168 }
169 else if (items == 2){ /* writer */
170 mouse_attr_set(aTHX_ self, mg, ST(1));
171 }
172 else{
173 mouse_throw_error(MOUSE_mg_attribute(mg), NULL,
174 "Expected exactly one or two argument for an accessor");
175 }
176}
177
178
30e11004 179XS(XS_Mouse_reader)
646c0371 180{
181 dVAR; dXSARGS;
182 dMOUSE_self;
183 MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
184
185 if (items != 1) {
186 mouse_throw_error(MOUSE_mg_attribute(mg), NULL,
187 "Cannot assign a value to a read-only accessor");
188 }
189
190 SP -= items; /* PPCODE */
191 PUTBACK;
192
193 mouse_attr_get(aTHX_ self, mg);
194}
195
30e11004 196XS(XS_Mouse_writer)
646c0371 197{
198 dVAR; dXSARGS;
199 dMOUSE_self;
200 MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
201
202 if (items != 2) {
203 mouse_throw_error(MOUSE_mg_attribute(mg), NULL,
204 "Too few arguments for a write-only accessor");
205 }
206
207 SP -= items; /* PPCODE */
208 PUTBACK;
209
210 mouse_attr_set(aTHX_ self, mg, ST(1));
211}
212
213/* simple accessors */
214
215/*
216static MAGIC*
217mouse_accessor_get_mg(pTHX_ CV* const xsub){
218 return moose_mg_find(aTHX_ (SV*)xsub, &mouse_simple_accessor_vtbl, MOOSEf_DIE_ON_FAIL);
219}
220*/
221
222CV*
223mouse_install_simple_accessor(pTHX_ const char* const fq_name, const char* const key, I32 const keylen, XSUBADDR_t const accessor_impl){
224 CV* const xsub = newXS((char*)fq_name, accessor_impl, __FILE__);
225 SV* const slot = newSVpvn_share(key, keylen, 0U);
226 MAGIC* mg;
227
228 if(!fq_name){
229 /* anonymous xsubs need sv_2mortal */
230 sv_2mortal((SV*)xsub);
231 }
232
233 mg = sv_magicext((SV*)xsub, slot, PERL_MAGIC_ext, &mouse_accessor_vtbl, NULL, 0);
234 SvREFCNT_dec(slot); /* sv_magicext() increases refcnt in mg_obj */
235
236 /* NOTE:
237 * although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx)
238 */
239 CvXSUBANY(xsub).any_ptr = (void*)mg;
240
241 return xsub;
242}
243
30e11004 244XS(XS_Mouse_simple_reader)
646c0371 245{
246 dVAR; dXSARGS;
247 dMOUSE_self;
248 SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr);
249 SV* value;
250
251 if (items != 1) {
252 croak("Expected exactly one argument for a reader for '%"SVf"'", slot);
253 }
254
6fe2272b 255 value = get_slot(self, slot);
646c0371 256 ST(0) = value ? value : &PL_sv_undef;
257 XSRETURN(1);
258}
259
260
30e11004 261XS(XS_Mouse_simple_writer)
646c0371 262{
263 dVAR; dXSARGS;
264 dMOUSE_self;
265 SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr);
266
267 if (items != 2) {
268 croak("Expected exactly two argument for a writer for '%"SVf"'", slot);
269 }
270
6fe2272b 271 ST(0) = set_slot(self, slot, ST(1));
646c0371 272 XSRETURN(1);
273}
274
30e11004 275XS(XS_Mouse_simple_clearer)
646c0371 276{
277 dVAR; dXSARGS;
278 dMOUSE_self;
279 SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr);
280 SV* value;
281
282 if (items != 1) {
283 croak("Expected exactly one argument for a clearer for '%"SVf"'", slot);
284 }
285
6fe2272b 286 value = delete_slot(self, slot);
646c0371 287 ST(0) = value ? value : &PL_sv_undef;
288 XSRETURN(1);
289}
290
30e11004 291XS(XS_Mouse_simple_predicate)
646c0371 292{
293 dVAR; dXSARGS;
294 dMOUSE_self;
295 SV* const slot = MOUSE_mg_slot((MAGIC*)XSANY.any_ptr);
296
297 if (items != 1) {
298 croak("Expected exactly one argument for a predicate for '%"SVf"'", slot);
299 }
300
6fe2272b 301 ST(0) = boolSV( has_slot(self, slot) );
646c0371 302 XSRETURN(1);
303}
304
76770976 305/* simple instance slot accessor (or Mouse::Meta::Instance) */
646c0371 306
307SV*
308mouse_instance_create(pTHX_ HV* const stash) {
309 assert(stash);
aa2d2e2c 310 assert(SvTYPE(stash) == SVt_PVHV);
646c0371 311 return sv_bless( newRV_noinc((SV*)newHV()), stash );
312}
313
314SV*
315mouse_instance_clone(pTHX_ SV* const instance) {
316 HV* proto;
317 assert(instance);
318
319 CHECK_INSTANCE(instance);
320 proto = newHVhv((HV*)SvRV(instance));
321 return sv_bless( newRV_noinc((SV*)proto), SvSTASH(SvRV(instance)) );
322}
323
324bool
325mouse_instance_has_slot(pTHX_ SV* const instance, SV* const slot) {
326 assert(instance);
327 assert(slot);
328 CHECK_INSTANCE(instance);
329 return hv_exists_ent((HV*)SvRV(instance), slot, 0U);
330}
331
332SV*
333mouse_instance_get_slot(pTHX_ SV* const instance, SV* const slot) {
334 HE* he;
335 assert(instance);
336 assert(slot);
337 CHECK_INSTANCE(instance);
338 he = hv_fetch_ent((HV*)SvRV(instance), slot, FALSE, 0U);
339 return he ? HeVAL(he) : NULL;
340}
341
342SV*
343mouse_instance_set_slot(pTHX_ SV* const instance, SV* const slot, SV* const value) {
344 HE* he;
345 SV* sv;
346 assert(instance);
347 assert(slot);
348 assert(value);
349 CHECK_INSTANCE(instance);
350 he = hv_fetch_ent((HV*)SvRV(instance), slot, TRUE, 0U);
351 sv = HeVAL(he);
352 sv_setsv_mg(sv, value);
353 return sv;
354}
355
356SV*
357mouse_instance_delete_slot(pTHX_ SV* const instance, SV* const slot) {
358 assert(instance);
359 assert(slot);
360 CHECK_INSTANCE(instance);
361 return hv_delete_ent((HV*)SvRV(instance), slot, 0, 0U);
362}
363
364void
365mouse_instance_weaken_slot(pTHX_ SV* const instance, SV* const slot) {
366 HE* he;
367 assert(instance);
368 assert(slot);
369 CHECK_INSTANCE(instance);
370 he = hv_fetch_ent((HV*)SvRV(instance), slot, FALSE, 0U);
371 if(he){
372 sv_rvweaken(HeVAL(he));
373 }
374}
375\r
376MODULE = Mouse::Meta::Method::Accessor::XS PACKAGE = Mouse::Meta::Method::Accessor::XS
377
378PROTOTYPES: DISABLE
379VERSIONCHECK: DISABLE
380
381CV*
382_generate_accessor(klass, SV* attr, metaclass)
383CODE:
384{
30e11004 385 RETVAL = mouse_instantiate_xs_accessor(aTHX_ attr, XS_Mouse_accessor);
646c0371 386}
387OUTPUT:
388 RETVAL
389
390CV*
391_generate_reader(klass, SV* attr, metaclass)
392CODE:
393{
30e11004 394 RETVAL = mouse_instantiate_xs_accessor(aTHX_ attr, XS_Mouse_reader);
646c0371 395}
396OUTPUT:
397 RETVAL
398
399CV*
400_generate_writer(klass, SV* attr, metaclass)
401CODE:
402{
30e11004 403 RETVAL = mouse_instantiate_xs_accessor(aTHX_ attr, XS_Mouse_writer);
646c0371 404}
405OUTPUT:
406 RETVAL
407
408CV*
409_generate_clearer(klass, SV* attr, metaclass)
410CODE:
411{
412 SV* const slot = mcall0s(attr, "name");
413 STRLEN len;
414 const char* const pv = SvPV_const(slot, len);
30e11004 415 RETVAL = mouse_install_simple_accessor(aTHX_ NULL, pv, len, XS_Mouse_simple_clearer);
646c0371 416}
417OUTPUT:
418 RETVAL
419
420CV*
421_generate_predicate(klass, SV* attr, metaclass)
422CODE:
423{
424 SV* const slot = mcall0s(attr, "name");
425 STRLEN len;
426 const char* const pv = SvPV_const(slot, len);
30e11004 427 RETVAL = mouse_install_simple_accessor(aTHX_ NULL, pv, len, XS_Mouse_simple_predicate);
646c0371 428}
429OUTPUT:
430 RETVAL
431