Commit | Line | Data |
1ea12c91 |
1 | #include "EXTERN.h" |
2 | #include "perl.h" |
3 | #include "XSUB.h" |
4 | |
1ea12c91 |
5 | /* FIXME |
6 | * needs to be made into Moose::XS::Meta::Instance and Meta::Slot for the |
7 | * metadata, with a proper destructor. XSANY still points to this struct, but |
8 | * it is shared by all functions of the same type. |
9 | * |
de2f2e97 |
10 | * Instance contains SvSTASH, and ATTR slots[] |
1ea12c91 |
11 | * |
de2f2e97 |
12 | * On recreation of the meta instance we refresh the ATTR value of all the CVs |
1ea12c91 |
13 | * we installed |
14 | * |
15 | * need a good way to handle time between invalidate and regeneration (just |
16 | * check XSANY and call get_meta_instance if null?) |
17 | */ |
18 | |
1ea12c91 |
19 | /* FIXME |
20 | * type constraints are already implemented by konobi |
21 | * should be trivial to do coercions for the core types, too |
22 | * |
23 | * TypeConstraint::Class can compare SvSTASH by ptr, and if it's neq *then* |
24 | * call ->isa (should handle vast majority of cases) |
25 | * |
26 | * base parametrized types are also trivial |
27 | * |
28 | * ClassName is get_stathpvn |
29 | */ |
30 | |
31 | /* FIXME |
de2f2e97 |
32 | * for a constructor we have ATTR *attrs, and iterate that, removing init_arg |
1ea12c91 |
33 | * we can preallocate the structure to the right size (maybe even with the |
34 | * right HEs?), and do various other prehashing hacks to gain speed |
35 | * */ |
36 | |
37 | /* FIXME |
38 | * delegations and attribute helpers: |
39 | * |
40 | * typedef struct { |
de2f2e97 |
41 | * ATTR *attr; |
1ea12c91 |
42 | * pv *method; |
43 | * } delegation; |
44 | * |
45 | * typedef struct { |
de2f2e97 |
46 | * ATTR *attr; |
1ea12c91 |
47 | * I32 *type; // hash, array, whatever + vtable for operation |
48 | * } attributehelper; |
49 | */ |
50 | |
de2f2e97 |
51 | |
52 | STATIC MGVTBL null_mg_vtbl = { |
53 | NULL, /* get */ |
54 | NULL, /* set */ |
55 | NULL, /* len */ |
56 | NULL, /* clear */ |
57 | NULL, /* free */ |
58 | #if MGf_COPY |
59 | NULL, /* copy */ |
60 | #endif /* MGf_COPY */ |
61 | #if MGf_DUP |
62 | NULL, /* dup */ |
63 | #endif /* MGf_DUP */ |
64 | #if MGf_LOCAL |
65 | NULL, /* local */ |
66 | #endif /* MGf_LOCAL */ |
67 | }; |
68 | |
69 | |
70 | |
71 | typedef enum { |
72 | Any = 0, |
73 | Item, |
74 | Bool, |
75 | Maybe, /* [`a] */ |
76 | Undef, |
77 | Defined, |
78 | Value, |
79 | Num, |
80 | Int, |
81 | Str, |
82 | ClassName, |
83 | Ref, |
84 | ScalarRef, |
85 | ArrayRef, /* [`a] */ |
86 | HashRef, /* [`a] */ |
87 | CodeRef, |
88 | RegexpRef, |
89 | GlobRef, |
90 | FileHandle, |
91 | Object, |
92 | Role, |
93 | |
94 | /* XS only types */ |
95 | Class, |
96 | |
97 | max_TC |
98 | } TC; |
99 | |
100 | typedef union { |
101 | TC type; |
102 | CV *cv; |
103 | HV *stash; |
104 | OP *op; |
105 | } TC_CHECK; |
106 | |
107 | typedef enum { |
108 | tc_none = 0, |
109 | tc_type, |
110 | tc_cv, |
111 | tc_stash, |
112 | tc_op, |
113 | } tc_kind; |
114 | |
115 | typedef union { |
116 | char *builder; |
117 | SV *value; |
118 | CV *sub; |
119 | OP *op; |
120 | U32 type; |
121 | } DEFAULT; |
122 | |
123 | typedef enum { |
124 | default_none = 0, |
125 | default_type, |
126 | default_builder, |
127 | default_value, |
128 | default_sub, |
129 | default_op, |
130 | } default_kind; |
131 | |
1ea12c91 |
132 | typedef struct { |
de2f2e97 |
133 | /* the meta instance struct */ |
134 | struct mi *mi; |
135 | |
136 | U32 flags; /* slot type, TC behavior, coerce, weaken, (no default | default, builder + lazy), auto_deref */ |
137 | |
138 | /* slot access fields */ |
139 | SV *slot_sv; /* value of the slot (slot name presumably) */ |
140 | U32 slot_u32; /* for optimized access (precomputed hash or otherr) */ |
141 | |
142 | DEFAULT def; /* cv, value or other, depending on flags */ |
143 | |
144 | TC_CHECK tc_check; /* cv, value or other, dependidng on flags */ |
145 | SV *type_constraint; /* meta attr */ |
146 | |
147 | CV *initializer; |
148 | CV *trigger; |
149 | |
150 | SV *attr; /* the meta attr object */ |
151 | AV *cvs; /* CVs which use this attr */ |
152 | } ATTR; |
153 | |
154 | /* slot flags: |
155 | * instance reading writing |
156 | * 00000000 00000000 00000000 00000000 |
157 | * ^ trigger |
158 | * ^ weak |
159 | * ^^^ tc_kind |
160 | * ^ coerce |
161 | * ^^^ default_kind |
162 | * ^ lazy |
163 | * ^ required |
164 | * ^^^^^^^ if 0 then nothing special (just hash)? FIXME TBD |
165 | */ |
166 | |
167 | #define ATTR_INSTANCE_MASK 0xff000000 |
168 | #define ATTR_READING_MASK 0x0000ff00 |
169 | #define ATTR_WRITING_MASK 0x000000ff |
170 | |
171 | #define ATTR_MASK_TYPE 0x7 |
172 | |
173 | #define ATTR_MASK_DEFAULT 0x700 |
174 | #define ATTR_SHIFT_DEAFULT 8 |
175 | |
176 | #define ATTR_LAZY 0x800 |
177 | |
178 | #define ATTR_COERCE 0x08 |
179 | #define ATTR_WEAK 0x10 |
180 | #define ATTR_TRIGGER 0x10 |
181 | |
182 | #define ATTR_ISWEAK(attr) ( attr->flags & ATTR_WEAK ) |
183 | #define ATTR_ISLAZY(attr) ( attr->flags & ATTR_LAZY ) |
184 | #define ATTR_ISCOERCE(attr) ( attr->flags & ATTR_COERCE ) |
1ea12c91 |
185 | |
de2f2e97 |
186 | #define ATTR_TYPE(f) ( attr->flags & 0x7 ) |
187 | #define ATTR_DEFAULT(f) ( ( attr->flags & ATTR_MASK_DEFAULT ) >> ATTR_SHIFT_DEFAULT ) |
1ea12c91 |
188 | |
de2f2e97 |
189 | #define ATTR_DUMB_READER(attr) !ATTR_IS_LAZY(attr) |
190 | #define ATTR_DUMB_WRITER(attr) ( ( attr->flags & ATTR_WRITING_MASK ) == 0 ) |
191 | #define ATTR_DUMB_INSTANCE(attr) ( ( attr->flags & ATTR_INSTANCE_MASK ) == 0 ) |
1ea12c91 |
192 | |
de2f2e97 |
193 | #define dATTR ATTR *attr = (INT2PTR(ATTR *, (XSANY.any_i32 || define_attr(aTHX_ cv)))) |
194 | |
195 | |
196 | /* FIXME define a vtable that does call_sv */ |
197 | typedef struct { |
198 | SV * (*get)(pTHX_ SV *self, ATTR *attr); |
199 | void (*set)(pTHX_ SV *self, ATTR *attr, SV *value); |
200 | bool * (*has)(pTHX_ SV *self, ATTR *attr); |
201 | SV * (*delete)(pTHX_ SV *self, ATTR *attr); |
202 | } instance_vtbl; |
203 | |
204 | |
205 | typedef enum { |
206 | hash = 0, |
207 | |
208 | /* these are not yet implemented */ |
209 | array, |
210 | fptr, |
211 | cv, |
212 | judy, |
213 | } instance_types; |
214 | |
215 | typedef struct mi { |
216 | SV *associated_metaclass; |
217 | HV *stash; |
218 | |
219 | /* slot access method */ |
220 | instance_types type; |
221 | instance_vtbl *vtbl; |
222 | |
223 | /* attr descriptors */ |
224 | I32 num_attrs; |
225 | ATTR *attrs; |
226 | } MI; |
227 | |
228 | |
229 | STATIC void init_attr (MI *mi, ATTR *attr, HV *desc) { |
1ea12c91 |
230 | U32 hash; |
231 | STRLEN len; |
de2f2e97 |
232 | SV **key = hv_fetchs(desc, "key", 0); |
233 | SV **meta_attr = hv_fetchs(desc, "meta", 0); |
234 | char *pv; |
235 | |
236 | if ( !meta_attr ) croak("'meta' is required"); |
237 | |
238 | attr->attr = *meta_attr; |
239 | |
240 | attr->mi = mi; |
241 | |
242 | attr->flags = 0; |
243 | |
244 | |
245 | /* if type == hash */ |
246 | /* prehash the key */ |
247 | if ( !key ) croak("'key' is required"); |
248 | |
249 | pv = SvPV(*key, len); |
1ea12c91 |
250 | |
251 | PERL_HASH(hash, pv, len); |
1ea12c91 |
252 | |
de2f2e97 |
253 | attr->slot_sv = newSVpvn_share(pv, len, hash); |
254 | attr->slot_u32 = hash; |
255 | |
256 | attr->def.type = 0; |
257 | |
258 | attr->tc_check.type = 0; |
259 | attr->type_constraint = NULL; |
260 | |
261 | |
262 | attr->initializer = NULL; |
263 | attr->trigger = NULL; |
264 | |
265 | /* cross refs to CVs which use this struct */ |
266 | attr->cvs = newAV(); |
267 | } |
268 | |
269 | STATIC MI *new_mi (pTHX_ HV *stash, SV *meta, AV *attrs) { |
270 | MI *mi; |
271 | I32 ix; |
272 | const I32 num = av_len(attrs) + 1; |
273 | |
274 | Newx(mi, 1, MI); |
275 | |
276 | SvREFCNT_inc_simple(stash); |
277 | mi->stash = stash; |
278 | |
279 | SvREFCNT_inc_simple(meta); |
280 | mi->associated_metaclass = meta; |
281 | |
282 | mi->type = 0; /* nothing else implemented yet */ |
283 | |
284 | /* initialize attributes */ |
285 | mi->num_attrs = num; |
286 | Newx(mi->attrs, num, ATTR); |
287 | for ( ix = 0; ix < mi->num_attrs; ix++ ) { |
288 | SV **desc = av_fetch(attrs, ix, 0); |
289 | |
290 | if ( !desc || !SvROK(*desc) || !(SvTYPE(SvRV(*desc)) == SVt_PVHV) ) |
291 | croak("Attribute descriptor has to be a hash reference"); |
292 | |
293 | init_attr(mi, &mi->attrs[ix], (HV *)SvRV(*desc)); |
294 | } |
295 | |
296 | return mi; |
297 | } |
298 | |
299 | STATIC SV *get_meta_attr_from_mg(pTHX_ CV *cv) { |
300 | MAGIC *mg, *moremagic; |
301 | |
302 | if (SvTYPE(cv) >= SVt_PVMG) { |
303 | for (mg = SvMAGIC(cv); mg; mg = mg->mg_moremagic) { |
304 | if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_virtual == &null_mg_vtbl)) |
305 | break; |
306 | } |
307 | if (mg) |
308 | return mg->mg_obj; |
309 | } |
310 | |
311 | croak("No attr found in magic!"); |
312 | return NULL; |
313 | } |
314 | |
315 | STATIC ATTR *get_attr(pTHX_ CV *cv) { |
316 | SV *meta_attr = get_meta_attr_from_mg(aTHX_ cv); |
317 | |
318 | #if 0 |
319 | my $mi = $meta_attr->associated_metaclass->get_meta_instance; |
320 | my @attrs = map { |
321 | { |
322 | meta => $_, |
323 | key => ($_->slots)[0], |
324 | }, |
325 | } @{ $mi->attributes }; |
326 | #else |
327 | croak("todo"); |
328 | #endif |
329 | |
330 | return NULL; |
1ea12c91 |
331 | } |
332 | |
de2f2e97 |
333 | STATIC ATTR *define_attr (pTHX_ CV *cv) { |
334 | ATTR *attr = get_attr(aTHX_ cv); |
335 | assert(attr); |
336 | |
337 | XSANY.any_i32 = PTR2IV(attr); |
338 | av_push( attr->cvs, cv ); |
339 | |
340 | return attr; |
341 | } |
342 | |
343 | STATIC void weaken(pTHX_ SV *sv) { |
1ea12c91 |
344 | #ifdef SvWEAKREF |
de2f2e97 |
345 | sv_rvweaken(sv); /* FIXME i think this might warn when weakening an already weak ref */ |
1ea12c91 |
346 | #else |
347 | croak("weak references are not implemented in this release of perl"); |
348 | #endif |
349 | } |
350 | |
351 | |
352 | /* meta instance protocol */ |
353 | |
de2f2e97 |
354 | STATIC SV *get_slot_value(pTHX_ SV *self, ATTR *attr) { |
1ea12c91 |
355 | HE *he; |
356 | |
357 | assert(self); |
358 | assert(SvROK(self)); |
359 | assert(SvTYPE(SvRV(self)) == SVt_PVHV); |
360 | |
de2f2e97 |
361 | assert( ATTR_DUMB_INSTANCE(attr) ); |
362 | |
363 | if ((he = hv_fetch_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32))) |
1ea12c91 |
364 | return HeVAL(he); |
365 | else |
366 | return NULL; |
367 | } |
368 | |
de2f2e97 |
369 | STATIC void set_slot_value(pTHX_ SV *self, ATTR *attr, SV *value) { |
1ea12c91 |
370 | HE *he; |
371 | |
372 | assert(self); |
373 | assert(SvROK(self)); |
374 | assert(SvTYPE(SvRV(self)) == SVt_PVHV); |
375 | |
de2f2e97 |
376 | assert( ATTR_DUMB_INSTANCE(attr) ); |
377 | |
1ea12c91 |
378 | SvREFCNT_inc(value); |
379 | |
de2f2e97 |
380 | he = hv_store_ent((HV*)SvRV(self), attr->slot_sv, value, attr->slot_u32); |
1ea12c91 |
381 | if (he != NULL) { |
de2f2e97 |
382 | if ( ATTR_ISWEAK(attr) ) |
383 | weaken(aTHX_ HeVAL(he)); /* actually only needed once at HE creation time */ |
1ea12c91 |
384 | } else { |
385 | croak("Hash store failed."); |
386 | } |
387 | } |
388 | |
de2f2e97 |
389 | STATIC bool has_slot_value(pTHX_ SV *self, ATTR *attr) { |
1ea12c91 |
390 | assert(self); |
391 | assert(SvROK(self)); |
392 | assert(SvTYPE(SvRV(self)) == SVt_PVHV); |
393 | |
de2f2e97 |
394 | assert( ATTR_DUMB_INSTANCE(attr) ); |
395 | |
396 | return hv_exists_ent((HV *)SvRV(self), attr->slot_sv, attr->slot_u32); |
397 | } |
398 | |
399 | STATIC SV *deinitialize_slot(pTHX_ SV *self, ATTR *attr) { |
400 | assert(self); |
401 | assert(SvROK(self)); |
402 | assert(SvTYPE(SvRV(self)) == SVt_PVHV); |
403 | |
404 | assert( ATTR_DUMB_INSTANCE(attr) ); |
405 | |
406 | return hv_delete_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32); |
1ea12c91 |
407 | } |
408 | |
409 | |
410 | /* simple high level api */ |
411 | |
de2f2e97 |
412 | STATIC XS(getter); |
413 | STATIC XS(getter) |
1ea12c91 |
414 | { |
415 | #ifdef dVAR |
416 | dVAR; |
417 | #endif |
418 | dXSARGS; |
de2f2e97 |
419 | dATTR; |
1ea12c91 |
420 | SV *value; |
421 | |
422 | if (items != 1) |
423 | Perl_croak(aTHX_ "Usage: %s(%s)", GvNAME(CvGV(cv)), "self"); |
424 | |
425 | SP -= items; |
426 | |
de2f2e97 |
427 | assert( ATTR_DUMB_READER(attr) ); |
428 | |
429 | value = get_slot_value(aTHX_ ST(0), attr); |
1ea12c91 |
430 | |
431 | if (value) { |
432 | ST(0) = sv_mortalcopy(value); /* mortalcopy because $_ .= "blah" for $foo->bar */ |
433 | XSRETURN(1); |
434 | } else { |
435 | XSRETURN_UNDEF; |
436 | } |
437 | } |
438 | |
de2f2e97 |
439 | STATIC XS(setter); |
440 | STATIC XS(setter) |
1ea12c91 |
441 | { |
442 | #ifdef dVAR |
443 | dVAR; |
444 | #endif |
445 | dXSARGS; |
de2f2e97 |
446 | dATTR; |
1ea12c91 |
447 | |
448 | if (items != 2) |
449 | Perl_croak(aTHX_ "Usage: %s(%s, %s)", GvNAME(CvGV(cv)), "self", "value"); |
450 | |
451 | SP -= items; |
452 | |
de2f2e97 |
453 | assert( ATTR_DUMB_WRITER(attr) ); |
454 | |
455 | set_slot_value(aTHX_ ST(0), attr, ST(1)); |
1ea12c91 |
456 | |
457 | ST(0) = ST(1); /* return value */ |
458 | XSRETURN(1); |
459 | } |
460 | |
de2f2e97 |
461 | STATIC XS(accessor); |
462 | STATIC XS(accessor) |
1ea12c91 |
463 | { |
464 | #ifdef dVAR |
465 | dVAR; |
466 | #endif |
467 | dXSARGS; |
de2f2e97 |
468 | dATTR; |
1ea12c91 |
469 | |
470 | if (items < 1) |
471 | Perl_croak(aTHX_ "Usage: %s(%s, [ %s ])", GvNAME(CvGV(cv)), "self", "value"); |
472 | |
473 | SP -= items; |
474 | |
475 | if (items > 1) { |
de2f2e97 |
476 | assert( ATTR_DUMB_READER(attr) ); |
477 | set_slot_value(aTHX_ ST(0), attr, ST(1)); |
1ea12c91 |
478 | ST(0) = ST(1); /* return value */ |
479 | } else { |
de2f2e97 |
480 | assert( ATTR_DUMB_WRITER(attr) ); |
481 | SV *value = get_slot_value(aTHX_ ST(0), attr); |
1ea12c91 |
482 | if ( value ) { |
483 | ST(0) = value; |
484 | } else { |
485 | XSRETURN_UNDEF; |
486 | } |
487 | } |
488 | |
489 | XSRETURN(1); |
490 | } |
491 | |
492 | STATIC XS(predicate); |
493 | STATIC XS(predicate) |
494 | { |
495 | #ifdef dVAR |
496 | dVAR; |
497 | #endif |
498 | dXSARGS; |
de2f2e97 |
499 | dATTR; |
1ea12c91 |
500 | |
501 | if (items != 1) |
502 | Perl_croak(aTHX_ "Usage: %s(%s)", GvNAME(CvGV(cv)), "self"); |
503 | |
504 | SP -= items; |
505 | |
de2f2e97 |
506 | if ( has_slot_value(aTHX_ ST(0), attr) ) |
1ea12c91 |
507 | XSRETURN_YES; |
508 | else |
509 | XSRETURN_NO; |
510 | } |
511 | |
512 | enum xs_body { |
de2f2e97 |
513 | xs_body_getter = 0, |
514 | xs_body_setter, |
515 | xs_body_accessor, |
1ea12c91 |
516 | xs_body_predicate, |
517 | max_xs_body |
518 | }; |
519 | |
520 | STATIC XSPROTO ((*xs_bodies[])) = { |
de2f2e97 |
521 | getter, |
522 | setter, |
523 | accessor, |
1ea12c91 |
524 | predicate, |
525 | }; |
526 | |
527 | MODULE = Moose PACKAGE = Moose::XS |
528 | |
529 | CV * |
de2f2e97 |
530 | new_sub(attr, name) |
1ea12c91 |
531 | INPUT: |
de2f2e97 |
532 | SV *attr; |
533 | SV *name; |
1ea12c91 |
534 | ALIAS: |
de2f2e97 |
535 | new_getter = xs_body_getter |
536 | new_setter = xs_body_setter |
537 | new_accessor = xs_body_accessor |
538 | new_predicate = xs_body_predicate |
1ea12c91 |
539 | PREINIT: |
540 | CV * cv; |
541 | CODE: |
542 | if ( ix >= max_xs_body ) |
543 | croak("Unknown Moose::XS body type"); |
544 | |
de2f2e97 |
545 | if ( !sv_isobject(attr) ) |
546 | croak("'attr' must be a Moose::Meta::Attribute"); |
547 | |
548 | cv = newXS(SvOK(name) ? SvPV_nolen(name) : NULL, xs_bodies[ix], __FILE__); |
1ea12c91 |
549 | |
550 | if (cv == NULL) |
551 | croak("Oi vey!"); |
552 | |
de2f2e97 |
553 | /* associate CV with meta attr */ |
554 | (void)Perl_sv_magicext(aTHX_ (SV *)cv, attr, PERL_MAGIC_ext, &null_mg_vtbl, STR_WITH_LEN("Moose::Meta::Attribute") ); |
555 | |
556 | /* this will be set on first call */ |
557 | XSANY.any_i32 = 0; |
1ea12c91 |
558 | |
559 | RETVAL = cv; |
560 | OUTPUT: |
561 | RETVAL |
562 | |
563 | |