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