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 | |
14 | |
15 | SV* |
16 | mouse_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 | |
29 | CV* |
30 | mouse_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 */ |
63 | static void |
64 | mouse_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 | |
110 | static void |
111 | mouse_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 | |
125 | static void |
126 | mouse_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 |
157 | XS(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 |
179 | XS(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 |
196 | XS(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 | /* |
216 | static MAGIC* |
217 | mouse_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 | |
222 | CV* |
223 | mouse_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 |
244 | XS(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 |
261 | XS(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 |
275 | XS(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 |
291 | XS(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 | |
307 | SV* |
308 | mouse_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 | |
314 | SV* |
315 | mouse_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 | |
324 | bool |
325 | mouse_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 | |
332 | SV* |
333 | mouse_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 | |
342 | SV* |
343 | mouse_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 | |
356 | SV* |
357 | mouse_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 | |
364 | void |
365 | mouse_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 |
376 | MODULE = Mouse::Meta::Method::Accessor::XS PACKAGE = Mouse::Meta::Method::Accessor::XS |
377 | |
378 | PROTOTYPES: DISABLE |
379 | VERSIONCHECK: DISABLE |
380 | |
381 | CV* |
382 | _generate_accessor(klass, SV* attr, metaclass) |
383 | CODE: |
384 | { |
30e11004 |
385 | RETVAL = mouse_instantiate_xs_accessor(aTHX_ attr, XS_Mouse_accessor); |
646c0371 |
386 | } |
387 | OUTPUT: |
388 | RETVAL |
389 | |
390 | CV* |
391 | _generate_reader(klass, SV* attr, metaclass) |
392 | CODE: |
393 | { |
30e11004 |
394 | RETVAL = mouse_instantiate_xs_accessor(aTHX_ attr, XS_Mouse_reader); |
646c0371 |
395 | } |
396 | OUTPUT: |
397 | RETVAL |
398 | |
399 | CV* |
400 | _generate_writer(klass, SV* attr, metaclass) |
401 | CODE: |
402 | { |
30e11004 |
403 | RETVAL = mouse_instantiate_xs_accessor(aTHX_ attr, XS_Mouse_writer); |
646c0371 |
404 | } |
405 | OUTPUT: |
406 | RETVAL |
407 | |
408 | CV* |
409 | _generate_clearer(klass, SV* attr, metaclass) |
410 | CODE: |
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 | } |
417 | OUTPUT: |
418 | RETVAL |
419 | |
420 | CV* |
421 | _generate_predicate(klass, SV* attr, metaclass) |
422 | CODE: |
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 | } |
429 | OUTPUT: |
430 | RETVAL |
431 | |