3 /* Moose XS Attribute object */
12 #define MOUSE_xa_attribute(m) MOUSE_av_at(m, MOUSE_XA_ATTRIBUTE)
13 #define MOUSE_xa_tc(m) MOUSE_av_at(m, MOUSE_XA_TC)
14 #define MOUSE_xa_tc_code(m) MOUSE_av_at(m, MOUSE_XA_TC_CODE)
16 #define MOUSE_mg_attribute(mg) MOUSE_xa_attribute(MOUSE_mg_xa(mg))
18 enum mouse_xa_flags_t{
19 MOUSEf_ATTR_HAS_TC = 0x0001,
20 MOUSEf_ATTR_HAS_DEFAULT = 0x0002,
21 MOUSEf_ATTR_HAS_BUILDER = 0x0004,
22 MOUSEf_ATTR_HAS_INITIALIZER = 0x0008, /* not used in Mouse */
23 MOUSEf_ATTR_HAS_TRIGGER = 0x0010,
25 MOUSEf_ATTR_IS_LAZY = 0x0020,
26 MOUSEf_ATTR_IS_WEAK_REF = 0x0040,
27 MOUSEf_ATTR_IS_REQUIRED = 0x0080,
29 MOUSEf_ATTR_SHOULD_COERCE = 0x0100,
31 MOUSEf_ATTR_SHOULD_AUTO_DEREF
33 MOUSEf_TC_IS_ARRAYREF = 0x0400,
34 MOUSEf_TC_IS_HASHREF = 0x0800,
36 MOUSEf_OTHER1 = 0x1000,
37 MOUSEf_OTHER2 = 0x2000,
38 MOUSEf_OTHER3 = 0x4000,
39 MOUSEf_OTHER4 = 0x8000,
41 MOUSEf_MOUSE_MASK = 0xFFFF /* not used */
44 static MGVTBL mouse_accessor_vtbl; /* MAGIC identity */
47 mouse_instantiate_xs_accessor(pTHX_ SV* const attr, XSUBADDR_t const accessor_impl){
48 SV* const slot = mcall0s(attr, "name");
49 AV* const xa = newAV();
56 xsub = newXS(NULL, accessor_impl, __FILE__);
57 sv_2mortal((SV*)xsub);
59 mg = sv_magicext((SV*)xsub, slot, PERL_MAGIC_ext, &mouse_accessor_vtbl, (char*)xa, HEf_SVKEY);
62 * although we use MAGIC for gc, we also store mg to CvXSUBANY for efficiency (gfx)
64 CvXSUBANY(xsub).any_ptr = (void*)mg;
66 av_extend(xa, MOUSE_XA_last - 1);
68 av_store(xa, MOUSE_XA_ATTRIBUTE, newSVsv(attr));
70 /* prepare attribute status */
71 /* XXX: making it lazy is a good way? */
73 if(SvTRUEx(mcall0s(attr, "has_type_constraint"))){
75 flags |= MOUSEf_ATTR_HAS_TC;
80 tc = mcall0s(attr, "type_constraint");
81 av_store(xa, MOUSE_XA_TC, newSVsv(tc));
83 if(SvTRUEx(mcall0s(attr, "should_auto_deref"))){
84 flags |= MOUSEf_ATTR_SHOULD_AUTO_DEREF;
85 if( SvTRUEx(mcall1s(tc, "is_a_type_of", newSVpvs_flags("ArrayRef", SVs_TEMP))) ){
86 flags |= MOUSEf_TC_IS_ARRAYREF;
88 else if( SvTRUEx(mcall1s(tc, "is_a_type_of", newSVpvs_flags("HashRef", SVs_TEMP))) ){
89 flags |= MOUSEf_TC_IS_HASHREF;
92 mouse_throw_error(attr, tc,
93 "Can not auto de-reference the type constraint '%"SVf"'",
98 if(SvTRUEx(mcall0s(attr, "should_coerce"))){
99 flags |= MOUSEf_ATTR_SHOULD_COERCE;
106 if(SvTRUEx(mcall0s(attr, "has_trigger"))){
107 flags |= MOUSEf_ATTR_HAS_TRIGGER;
110 if(SvTRUEx(mcall0s(attr, "is_lazy"))){
111 flags |= MOUSEf_ATTR_IS_LAZY;
113 if(SvTRUEx(mcall0s(attr, "has_builder"))){
114 flags |= MOUSEf_ATTR_HAS_BUILDER;
116 else if(SvTRUEx(mcall0s(attr, "has_default"))){
117 flags |= MOUSEf_ATTR_HAS_DEFAULT;
121 if(SvTRUEx(mcall0s(attr, "is_weak_ref"))){
122 flags |= MOUSEf_ATTR_IS_WEAK_REF;
125 if(SvTRUEx(mcall0s(attr, "is_required"))){
126 flags |= MOUSEf_ATTR_IS_REQUIRED;
129 MOUSE_mg_flags(mg) = flags;
135 mouse_apply_type_constraint(pTHX_ AV* const xa, SV* value, U16 const flags){
136 SV* const tc = MOUSE_xa_tc(xa);
139 if(flags & MOUSEf_ATTR_SHOULD_COERCE){
140 value = mcall1s(tc, "coerce", value);
143 if(!SvOK(MOUSE_xa_tc_code(xa))){
144 XS(XS_Mouse__Util__TypeConstraints_Item); /* prototype defined in Mouse.xs */
146 tc_code = mcall0s(tc, "_compiled_type_constraint");
148 if(SvROK(tc_code) && SvTYPE(SvRV(tc_code))
149 && CvXSUB((CV*)SvRV(tc_code)) == XS_Mouse__Util__TypeConstraints_Item){
150 /* built-in type constraints */
151 mouse_tc const id = CvXSUBANY((CV*)SvRV(tc_code)).any_i32;
152 av_store(xa, MOUSE_XA_TC_CODE, newSViv(id));
155 av_store(xa, MOUSE_XA_TC_CODE, newSVsv(tc_code));
159 tc_code = MOUSE_xa_tc_code(xa);
162 if(!mouse_tc_check(aTHX_ tc_code, value)){
163 mouse_throw_error(MOUSE_xa_attribute(xa), value,
164 "Attribute (%"SVf") does not pass the type constraint because: %"SVf,
165 mcall0s(MOUSE_xa_attribute(xa), "name"),
166 mcall1s(tc, "get_message", value));
173 /* pushes return values, does auto-deref if needed */
175 mouse_push_values(pTHX_ SV* const value, U16 const flags){
178 if(flags & MOUSEf_ATTR_SHOULD_AUTO_DEREF && GIMME_V == G_ARRAY){
179 if(!(value && SvOK(value))){
183 if(flags & MOUSEf_TC_IS_ARRAYREF){
184 AV* const av = (AV*)SvRV(value);
188 if(SvTYPE(av) != SVt_PVAV){
189 croak("Mouse-panic: Not an ARRAY reference");
192 len = av_len(av) + 1;
194 for(i = 0; i < len; i++){
195 SV** const svp = av_fetch(av, i, FALSE);
196 PUSHs(svp ? *svp : &PL_sv_undef);
199 else if(flags & MOUSEf_TC_IS_HASHREF){
200 HV* const hv = (HV*)SvRV(value);
203 if(SvTYPE(hv) != SVt_PVHV){
204 croak("Mouse-panic: Not a HASH reference");
208 while((he = hv_iternext(hv))){
210 PUSHs(hv_iterkeysv(he));
211 PUSHs(hv_iterval(hv, he));
216 XPUSHs(value ? value : &PL_sv_undef);
223 mouse_attr_get(pTHX_ SV* const self, MAGIC* const mg){
224 U16 const flags = MOUSE_mg_flags(mg);
225 SV* const slot = MOUSE_mg_slot(mg);
228 value = mouse_instance_get_slot(aTHX_ self, slot);
231 if( !value && flags & MOUSEf_ATTR_IS_LAZY ){
232 AV* const xa = MOUSE_mg_xa(mg);
233 SV* const attr = MOUSE_xa_attribute(xa);
235 /* get default value by $attr->default or $attr->builder */
236 if(flags & MOUSEf_ATTR_HAS_DEFAULT){
237 value = mcall0s(attr, "default");
239 if(SvROK(value) && SvTYPE(SvRV(value)) == SVt_PVCV){
240 value = mcall0(self, value);
243 else if(flags & MOUSEf_ATTR_HAS_BUILDER){
244 SV* const builder = mcall0s(attr, "builder");
245 value = mcall0(self, builder);
249 value = sv_newmortal();
252 /* apply coerce and type constraint */
253 if(flags & MOUSEf_ATTR_HAS_TC){
254 value = mouse_apply_type_constraint(aTHX_ xa, value, flags);
257 /* store value to slot */
258 value = mouse_instance_set_slot(aTHX_ self, slot, value);
261 mouse_push_values(aTHX_ value, flags);
265 mouse_attr_set(pTHX_ SV* const self, MAGIC* const mg, SV* value){
266 U16 const flags = MOUSE_mg_flags(mg);
267 SV* const slot = MOUSE_mg_slot(mg);
269 if(flags & MOUSEf_ATTR_HAS_TC){
270 value = mouse_apply_type_constraint(aTHX_ MOUSE_mg_xa(mg), value, flags);
273 mouse_instance_set_slot(aTHX_ self, slot, value);
275 if(flags & MOUSEf_ATTR_IS_WEAK_REF){
276 mouse_instance_weaken_slot(aTHX_ self, slot);
279 if(flags & MOUSEf_ATTR_HAS_TRIGGER){
280 SV* const trigger = mcall0s(MOUSE_mg_attribute(mg), "trigger");
289 call_sv(trigger, G_VOID | G_DISCARD);
290 /* need not SPAGAIN */
293 mouse_push_values(aTHX_ value, flags);
296 XS(mouse_xs_accessor)
300 MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
302 SP -= items; /* PPCODE */
305 if(items == 1){ /* reader */
306 mouse_attr_get(aTHX_ self, mg);
308 else if (items == 2){ /* writer */
309 mouse_attr_set(aTHX_ self, mg, ST(1));
312 mouse_throw_error(MOUSE_mg_attribute(mg), NULL,
313 "Expected exactly one or two argument for an accessor");
322 MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
325 mouse_throw_error(MOUSE_mg_attribute(mg), NULL,
326 "Cannot assign a value to a read-only accessor");
329 SP -= items; /* PPCODE */
332 mouse_attr_get(aTHX_ self, mg);
339 MAGIC* const mg = (MAGIC*)XSANY.any_ptr;
342 mouse_throw_error(MOUSE_mg_attribute(mg), NULL,
343 "Too few arguments for a write-only accessor");
346 SP -= items; /* PPCODE */
349 mouse_attr_set(aTHX_ self, mg, ST(1));