more tests
[gitmo/Moose.git] / Moose.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #define NEED_newRV_noinc
6 #define NEED_newSVpvn_share
7 #define NEED_sv_2pv_flags
8 #include "ppport.h"
9
10 #ifndef XSPROTO
11 #define XSPROTO(name) void name(pTHX_ CV* cv)
12 #endif
13
14 #ifndef gv_stashpvs
15 #define gv_stashpvs(x, y) gv_stashpvn(STR_WITH_LEN(x), y)
16 #endif
17
18 /* FIXME
19  * type constraints are already implemented by konobi
20  * should be trivial to do coercions for the core types, too
21  *
22  * TypeConstraint::Class can compare SvSTASH by ptr, and if it's neq *then*
23  * call ->isa (should handle vast majority of cases)
24  *
25  * base parametrized types are also trivial
26  *
27  * ClassName is get_stathpvn
28  */
29
30 /* FIXME
31  * for a constructor we have ATTR *attrs, and iterate that, removing init_arg
32  * we can preallocate the structure to the right size (maybe even with the
33  * right HEs?), and do various other prehashing hacks to gain speed
34  * */
35
36 /* FIXME
37  * delegations and attribute helpers:
38  *
39  * typedef struct {
40  *      ATTR *attr;
41  *      pv *method;
42  * } delegation;
43  *
44  * typedef struct {
45  *      ATTR *attr;
46  *      I32 *type; // hash, array, whatever + vtable for operation
47  * } attributehelper;
48  */
49
50
51 STATIC MGVTBL null_mg_vtbl = {
52     NULL, /* get */
53     NULL, /* set */
54     NULL, /* len */
55     NULL, /* clear */
56     NULL, /* free */
57 #if MGf_COPY
58     NULL, /* copy */
59 #endif /* MGf_COPY */
60 #if MGf_DUP
61     NULL, /* dup */
62 #endif /* MGf_DUP */
63 #if MGf_LOCAL
64     NULL, /* local */
65 #endif /* MGf_LOCAL */
66 };
67
68 STATIC MAGIC *stash_in_mg (pTHX_ SV *sv, SV *obj) {
69     MAGIC *mg = sv_magicext(sv, obj, PERL_MAGIC_ext, &null_mg_vtbl, NULL, 0 );
70     mg->mg_flags |= MGf_REFCOUNTED;
71
72     return mg;
73 }
74
75 STATIC SV *get_stashed_in_mg(pTHX_ SV *sv) {
76     MAGIC *mg, *moremagic;
77
78     if (SvTYPE(sv) >= SVt_PVMG) {
79         for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
80             if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_virtual == &null_mg_vtbl))
81                 break;
82         }
83         if (mg)
84             return mg->mg_obj;
85     }
86
87     return NULL;
88 }
89
90 /* this is a enum of checks */
91 typedef enum {
92     Any, /* or item, or bool */
93     Undef,
94     Defined,
95     Str, /* or value */
96     Num,
97     Int,
98     GlobRef, /* SVt_PVGV */
99     ArrayRef, /* SVt_PVAV */
100     HashRef, /* SVt_PVHV */
101     CodeRef, /* SVt_PVCV */
102     Ref,
103     ScalarRef,
104     FileHandle,
105     RegexpRef,
106     Object,
107     ClassName,
108     /* complex checks */
109     Role,
110     Enum,
111 } TC;
112
113 typedef enum {
114     tc_none = 0,
115     tc_type,
116     tc_stash,
117     tc_cv,
118     tc_op,
119     tc_fptr,
120 } tc_kind;
121
122 typedef union {
123     TC type;
124     SV *sv;
125     OP *op;
126     bool (*fptr)(pTHX_ SV *type_constraint, SV *sv);
127 } TC_CHECK;
128
129 typedef union {
130     char *builder;
131     SV *value;
132     CV *sub;
133     OP *op;
134     U32 type;
135 } DEFAULT;
136
137 typedef enum {
138     default_none = 0,
139     default_type,
140     default_builder,
141     default_value,
142     default_sub,
143     default_op,
144 } default_kind;
145
146 typedef struct {
147     /* the meta instance struct */
148     struct mi *mi;
149
150     U32 flags; /* slot type, TC behavior, coerce, weaken, (no default | default, builder + lazy), auto_deref */
151
152     /* slot access fields */
153     SV *slot_sv; /* value of the slot (slot name presumably) */
154     U32 slot_u32; /* for optimized access (precomputed hash or otherr) */
155
156     DEFAULT def; /* cv, value or other, depending on flags */
157
158     TC_CHECK tc_check; /* cv, value or other, dependidng on flags */
159     SV *type_constraint; /* meta attr */
160
161     CV *initializer;
162     CV *trigger;
163
164     SV *meta_attr; /* the meta attr object */
165     AV *cvs; /* CVs which use this attr */
166 } ATTR;
167
168 /* slot flags:
169  * instance           reading  writing
170  * 00000000 00000000 00000000 00000000
171  *                             ^       trigger
172  *                              ^      weak
173  *                               ^     tc refcnt
174  *                                 ^^^ tc_kind
175  *                                ^    coerce
176  *                        ^^^          default_kind
177  *                       ^             lazy
178  *                 ^                   required
179  * ^^^^^^^                             if 0 then nothing special (just hash)? FIXME TBD
180  */
181
182 #define ATTR_INSTANCE_MASK 0xff000000
183 #define ATTR_READING_MASK  0x0000ff00
184 #define ATTR_WRITING_MASK  0x000000ff
185
186 #define ATTR_MASK_TYPE 0x7
187
188 #define ATTR_MASK_DEFAULT 0x700
189 #define ATTR_SHIFT_DEAFULT 8
190
191 #define ATTR_LAZY 0x800
192
193 #define ATTR_COERCE 0x8
194 #define ATTR_TCREFCNT 0x10
195 #define ATTR_WEAK 0x20
196 #define ATTR_TRIGGER 0x40
197
198 #define ATTR_ISWEAK(attr) ( attr->flags & ATTR_WEAK )
199 #define ATTR_ISLAZY(attr) ( attr->flags & ATTR_LAZY )
200 #define ATTR_ISCOERCE(attr) ( attr->flags & ATTR_COERCE )
201
202 #define ATTR_TYPE(f) ( attr->flags & 0x7 )
203 #define ATTR_DEFAULT(f) ( ( attr->flags & ATTR_MASK_DEFAULT ) >> ATTR_SHIFT_DEFAULT )
204
205 #define ATTR_DUMB_READER(attr) !ATTR_IS_LAZY(attr)
206 #define ATTR_DUMB_WRITER(attr) ( ( attr->flags & ATTR_WRITING_MASK ) == 0 )
207 #define ATTR_DUMB_INSTANCE(attr) ( ( attr->flags & ATTR_INSTANCE_MASK ) == 0 )
208
209 #define dATTR ATTR *attr = (XSANY.any_i32 ? INT2PTR(ATTR *, (XSANY.any_i32)) : define_attr(aTHX_ cv))
210
211
212 /* FIXME define a vtable that does call_sv */
213 typedef struct {
214     SV * (*get)(pTHX_ SV *self, ATTR *attr);
215     void (*set)(pTHX_ SV *self, ATTR *attr, SV *value);
216     bool * (*has)(pTHX_ SV *self, ATTR *attr);
217     SV * (*delete)(pTHX_ SV *self, ATTR *attr);
218 } instance_vtbl;
219
220
221 typedef enum {
222     hash = 0,
223
224     /* these are not yet implemented */
225     array,
226     fptr,
227     cv,
228     judy,
229 } instance_types;
230
231 typedef struct mi {
232     HV *stash;
233
234     /* slot access method */
235     instance_types type;
236     instance_vtbl *vtbl;
237
238     /* attr descriptors */
239     I32 num_attrs;
240     ATTR *attrs;
241 } MI;
242
243
244
245
246 STATIC bool check_is_scalar_ref(SV *sv) {
247     if( SvROK(sv) ) {
248         switch (SvTYPE(SvRV(sv))) {
249             case SVt_IV:
250             case SVt_NV:
251             case SVt_PV:
252             case SVt_NULL:
253                 return 1;
254                 break;
255             default:
256                 return 0;
257         }
258     }
259     return 0;
260 }
261
262 STATIC bool check_reftype(TC type, SV *sv) {
263     int svt;
264
265     if ( !SvROK(sv) )
266         return 0;
267
268     switch (type) {
269         case GlobRef:
270             svt = SVt_PVGV;
271             break;
272         case ArrayRef:
273             svt = SVt_PVAV;
274             break;
275         case HashRef:
276             svt = SVt_PVHV;
277             break;
278         case CodeRef:
279             svt = SVt_PVCV;
280             break;
281     }
282
283     return SvTYPE(SvRV(sv)) == svt;
284 }
285
286 STATIC bool check_sv_class(pTHX_ HV *stash, SV *sv) {
287     dSP;
288     bool ret;
289     SV *rv;
290
291     if (!sv)
292         return 0;
293     SvGETMAGIC(sv);
294     if (!SvROK(sv))
295         return 0;
296     rv = (SV*)SvRV(sv);
297     if (!SvOBJECT(rv))
298         return 0;
299     if (SvSTASH(rv) == stash)
300         return 1;
301
302     ENTER;
303     SAVETMPS;
304     PUSHMARK(SP);
305     XPUSHs(sv);
306     XPUSHs(sv_2mortal(newSVpv(HvNAME_get(stash), 0)));
307     PUTBACK;
308
309     call_method("isa", G_SCALAR);
310
311     SPAGAIN;
312     ret = SvTRUE(TOPs);
313
314     FREETMPS;
315     LEAVE;
316
317     return ret;
318 }
319
320 STATIC bool check_sv_type (TC type, SV *sv) {
321     if (!sv)
322         return 0;
323
324     switch (type) {
325         case Any:
326             return 1;
327             break;
328         case Undef:
329             return !SvOK(sv);
330             break;
331         case Defined:
332             return SvOK(sv);
333             break;
334         case Str:
335             return (SvOK(sv) && !SvROK(sv));
336         case Num:
337 #if (PERL_VERSION < 8) || (PERL_VERSION == 8 && PERL_SUBVERSION <5)
338             if (!SvPOK(sv) && !SvPOKp(sv))
339                 return SvFLAGS(sv) & (SVf_NOK|SVp_NOK|SVf_IOK|SVp_IOK);
340             else
341 #endif
342                 return looks_like_number(sv);
343             break;
344         case Int:
345             if ( SvIOK(sv) ) {
346                 return 1;
347             } else if ( SvPOK(sv) ) {
348                 int i;
349                 STRLEN len;
350                 char *pv = SvPV(sv, len);
351                 char *end = pv + len;
352
353                 errno = 0;
354                 i = strtol(pv, &end, 0);
355                 return !errno;
356             }
357             return 0;
358             break;
359         case Ref:
360             return SvROK(sv);
361             break;
362         case ScalarRef:
363             return check_is_scalar_ref(sv);
364             break;
365         case ArrayRef:
366         case HashRef:
367         case CodeRef:
368         case GlobRef:
369             return check_reftype(type, sv);
370             break;
371         case Object:
372             return sv_isobject(sv);
373             break;
374         case ClassName:
375             if ( SvOK(sv) && !SvROK(sv) ) {
376                 STRLEN len;
377                 char *pv;
378                 pv = SvPV(sv, len);
379                 return ( gv_stashpvn(pv, len, 0) != NULL );
380             }
381             return 0;
382             break;
383         case RegexpRef:
384             return sv_isa(sv, "Regexp");
385             break;
386         case FileHandle:
387             croak("todo");
388             break;
389         default:
390             croak("todo");
391     }
392
393     return 0;
394 }
395
396 STATIC bool check_type_constraint(pTHX_ tc_kind kind, TC_CHECK tc_check, SV *type_constraint, SV *sv) {
397     switch (kind) {
398         case tc_none:
399             return 1;
400             break;
401         case tc_type:
402             return check_sv_type(tc_check.type, sv);
403             break;
404         case tc_stash:
405             return check_sv_class(aTHX_ (HV *)tc_check.sv, sv);
406             break;
407         case tc_fptr:
408             return tc_check.fptr(aTHX_ type_constraint, sv);
409             break;
410         case tc_cv:
411         case tc_op:
412             croak("todo");
413             break;
414     }
415
416     croak("todo");
417     return 0;
418 }
419
420
421 STATIC void init_attr (MI *mi, ATTR *attr, AV *desc) {
422     U32 flags = 0;
423     U32 hash;
424     STRLEN len;
425     char *pv;
426     I32 ix = av_len(desc);
427     SV **params = AvARRAY(desc);
428     SV *tc;
429     SV *key;
430
431     attr->mi = mi;
432
433
434     if ( ix != 12 )
435         croak("wrong number of args (%d != 13)", ix + 1);
436
437     for ( ; ix >= 0; ix-- ) {
438         if ( !params[ix] || params[ix] == &PL_sv_undef )
439             croak("bad params");
440     }
441
442     if ( !SvROK(params[1]) || SvTYPE(SvRV(params[1])) != SVt_PVAV )
443         croak("slots is not an array");
444
445     if ( av_len((AV *)SvRV(params[1])) != 0 )
446         croak("Only unary slots are supported at the moment");
447
448     /* calculate a hash from the slot */
449     /* FIXME arrays etc should also be supported */
450     key = *av_fetch((AV *)SvRV(params[1]), 0, 0);
451     pv = SvPV(key, len);
452     PERL_HASH(hash, pv, len);
453
454
455     /* FIXME better organize these */
456     if ( SvTRUE(params[2]) )
457         flags |= ATTR_WEAK;
458
459     if ( SvTRUE(params[3]) )
460         flags |= ATTR_COERCE;
461
462     if ( SvTRUE(params[4]) )
463         flags |= ATTR_LAZY;
464
465     tc = params[5];
466
467     if ( SvOK(tc) ) {
468         int tc_kind = SvIV(params[6]);
469         SV *data = params[7];
470
471         switch (tc_kind) {
472             case tc_type:
473                 attr->tc_check.type = SvIV(data);
474                 break;
475             case tc_stash:
476                 flags |= ATTR_TCREFCNT;
477                 attr->tc_check.sv = (SV *)gv_stashsv(data, 0);
478                 break;
479             case tc_cv:
480                 flags |= ATTR_TCREFCNT;
481                 attr->tc_check.sv = SvRV(data);
482                 if ( SvTYPE(attr->tc_check.sv) != SVt_PVCV )
483                     croak("compiled type constraint is not a coderef");
484                 break;
485             default:
486                 croak("todo");
487         }
488
489         flags |= tc_kind;
490     }
491
492     attr->flags = flags; /* FIXME default_kind */
493
494     attr->trigger = SvROK(params[6]) ? (CV *)SvRV(params[6]) : NULL;
495     if ( attr->trigger && SvTYPE(attr->trigger) != SVt_PVCV )
496         croak("trigger is not a coderef");
497
498     attr->initializer = SvROK(params[7]) ? (CV *)SvRV(params[7]) : NULL;
499     if ( attr->initializer && SvTYPE(attr->initializer) != SVt_PVCV )
500         croak("initializer is not a coderef");
501
502     /* copy refs */
503     attr->meta_attr       = newSVsv(params[0]);
504     attr->type_constraint = newSVsv(tc);
505     SvREFCNT_inc(attr->trigger);
506     SvREFCNT_inc(attr->initializer);
507     if ( flags & ATTR_TCREFCNT ) SvREFCNT_inc(attr->tc_check.sv);
508
509     attr->slot_sv = newSVpvn_share(pv, len, hash);
510     attr->slot_u32 = hash;
511
512     attr->def.type = 0;
513
514     /* cross refs to CVs which use this struct */
515     attr->cvs = newAV();
516 }
517
518 STATIC MI *new_mi (pTHX_ HV *stash, AV *attrs) {
519     MI *mi;
520     I32 ix;
521     const I32 num = av_len(attrs) + 1;
522
523     Newx(mi, 1, MI);
524
525     SvREFCNT_inc_simple(stash);
526     mi->stash = stash;
527
528     mi->type = 0; /* nothing else implemented yet */
529
530     /* initialize attributes */
531     mi->num_attrs = num;
532     Newx(mi->attrs, num, ATTR);
533     for ( ix = 0; ix < num; ix++ ) {
534         SV **desc = av_fetch(attrs, ix, 0);
535
536         if ( !desc || !*desc || !SvROK(*desc) || !(SvTYPE(SvRV(*desc)) == SVt_PVAV) ) {
537             croak("Attribute descriptor has to be a hash reference");
538         }
539
540         init_attr(mi, &mi->attrs[ix], (AV *)SvRV(*desc));
541     }
542
543     return mi;
544 }
545
546 STATIC void delete_mi (pTHX_ MI *mi) {
547     I32 i, j;
548
549     for ( i = 0; i < mi->num_attrs; i++ ) {
550         ATTR *attr = &mi->attrs[i];
551         /* clear the pointers to this meta attr from all the CVs */
552         SV **cvs = AvARRAY(attr->cvs);
553         for ( j = av_len(attr->cvs); j >= 0; j-- ) {
554             CV *cv = cvs[j];
555             XSANY.any_i32 = 0;
556         }
557
558         SvREFCNT_dec(attr->cvs);
559         SvREFCNT_dec(attr->slot_sv);
560         SvREFCNT_dec(attr->type_constraint);
561         if ( attr->flags & ATTR_TCREFCNT ) SvREFCNT_dec(attr->tc_check.sv);
562         SvREFCNT_dec(attr->initializer);
563         SvREFCNT_dec(attr->trigger);
564         SvREFCNT_dec(attr->meta_attr);
565     }
566
567     Safefree(mi->attrs);
568     Safefree(mi);
569 }
570
571 STATIC SV *new_mi_obj (pTHX_ MI *mi) {
572     HV *stash = gv_stashpvs("Moose::XS::Meta::Instance",0);
573     SV *obj = newRV_noinc(newSViv(PTR2IV(mi)));
574     sv_bless( obj, stash );
575     return obj;
576 }
577
578 STATIC SV *attr_to_meta_instance(pTHX_ SV *meta_attr) {
579     dSP;
580     I32 count;
581     SV *mi;
582
583     if ( !meta_attr )
584         croak("No attr found in magic!");
585
586     ENTER;
587     SAVETMPS;
588     PUSHMARK(SP);
589     XPUSHs(meta_attr);
590     PUTBACK;
591     count = call_pv("Moose::XS::attr_to_meta_instance", G_SCALAR);
592
593     if ( count != 1 )
594         croak("attr_to_meta_instance borked (%d args returned, expecting 1)", count);
595
596     SPAGAIN;
597     mi = POPs;
598
599     SvREFCNT_inc(mi);
600
601     PUTBACK;
602     FREETMPS;
603     LEAVE;
604
605     return mi;
606 }
607
608 STATIC SV *perl_mi_to_c_mi(pTHX_ SV *perl_mi) {
609     dSP;
610     I32 count;
611     MI *mi = NULL;
612     SV *class;
613     SV *attrs;
614     HV *stash;
615
616     ENTER;
617     SAVETMPS;
618     PUSHMARK(SP);
619     XPUSHs(perl_mi);
620     PUTBACK;
621     count = call_pv("Moose::XS::meta_instance_to_attr_descs", G_ARRAY);
622
623     if ( count != 2 )
624         croak("meta_instance_to_attr_descs borked (%d args returned, expecting 2)", count);
625
626     SPAGAIN;
627     attrs = POPs;
628     class = POPs;
629
630     PUTBACK;
631
632     stash = gv_stashsv(class, 0);
633
634     mi = new_mi(aTHX_ stash, (AV *)SvRV(attrs));
635
636     FREETMPS;
637     LEAVE;
638
639     return new_mi_obj(aTHX_ mi);
640 }
641
642 STATIC ATTR *mi_find_attr(MI *mi, SV *meta_attr) {
643     I32 ix;
644
645     for ( ix = 0; ix < mi->num_attrs; ix++ ) {
646         if ( SvRV(mi->attrs[ix].meta_attr) == SvRV(meta_attr) ) {
647             return &mi->attrs[ix];
648         }
649     }
650
651     sv_dump(meta_attr);
652     croak("Attr not found");
653     return NULL;
654 }
655
656 STATIC ATTR *get_attr(pTHX_ CV *cv) {
657     SV *meta_attr = get_stashed_in_mg(aTHX_ (SV *)cv);
658     SV *perl_mi = attr_to_meta_instance(aTHX_ meta_attr);
659     SV *c_mi = get_stashed_in_mg(aTHX_ SvRV(perl_mi));
660     MI *mi;
661
662     if (!c_mi) {
663         c_mi = perl_mi_to_c_mi(aTHX_ perl_mi);
664         stash_in_mg(aTHX_ SvRV(perl_mi), c_mi);
665         SvREFCNT_dec(c_mi);
666     }
667
668     sv_2mortal(perl_mi);
669
670     mi = INT2PTR(MI *, SvIV(SvRV(c_mi)));
671
672     return mi_find_attr(mi, meta_attr);
673 }
674
675 STATIC ATTR *define_attr (pTHX_ CV *cv) {
676     ATTR *attr = get_attr(aTHX_ cv);
677     assert(attr);
678
679     XSANY.any_i32 = PTR2IV(attr);
680
681     SvREFCNT_inc(cv);
682     av_push( attr->cvs, (SV *)cv );
683
684     return attr;
685 }
686
687 STATIC void weaken(pTHX_ SV *sv) {
688 #ifdef SvWEAKREF
689         sv_rvweaken(sv); /* FIXME i think this might warn when weakening an already weak ref */
690 #else
691         croak("weak references are not implemented in this release of perl");
692 #endif
693 }
694
695
696 /* meta instance protocol */
697
698 STATIC SV *get_slot_value(pTHX_ SV *self, ATTR *attr) {
699     HE *he;
700
701     assert(self);
702     assert(SvROK(self));
703     assert(SvTYPE(SvRV(self)) == SVt_PVHV);
704
705     assert( ATTR_DUMB_INSTANCE(attr) );
706
707     if ((he = hv_fetch_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32)))
708         return HeVAL(he);
709     else
710         return NULL;
711 }
712
713 STATIC void set_slot_value(pTHX_ SV *self, ATTR *attr, SV *value) {
714     HE *he;
715     SV *copy;
716
717     assert(self);
718     assert(SvROK(self));
719     assert(SvTYPE(SvRV(self)) == SVt_PVHV);
720
721     assert( ATTR_DUMB_INSTANCE(attr) );
722
723     copy = newSVsv(value);
724
725     he = hv_store_ent((HV*)SvRV(self), attr->slot_sv, copy, attr->slot_u32);
726
727     if (he != NULL) {
728         if ( ATTR_ISWEAK(attr) )
729             weaken(aTHX_ HeVAL(he));
730     } else {
731         SvREFCNT_dec(copy);
732         croak("Hash store failed.");
733     }
734 }
735
736 STATIC bool has_slot_value(pTHX_ SV *self, ATTR *attr) {
737     assert(self);
738     assert(SvROK(self));
739     assert(SvTYPE(SvRV(self)) == SVt_PVHV);
740
741     assert( ATTR_DUMB_INSTANCE(attr) );
742
743     return hv_exists_ent((HV *)SvRV(self), attr->slot_sv, attr->slot_u32);
744 }
745
746 STATIC SV *deinitialize_slot(pTHX_ SV *self, ATTR *attr) {
747     assert(self);
748     assert(SvROK(self));
749     assert(SvTYPE(SvRV(self)) == SVt_PVHV);
750
751     assert( ATTR_DUMB_INSTANCE(attr) );
752
753     return hv_delete_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32);
754 }
755
756 STATIC SV *getter_common(pTHX_ SV *self, ATTR *attr) {
757     assert( ATTR_DUMB_READER(attr) );
758     return get_slot_value(aTHX_ self, attr);
759 }
760
761 STATIC void setter_common(pTHX_ SV *self, ATTR *attr, SV *value) {
762     if ( attr->flags & ATTR_MASK_TYPE ) {
763         if ( !check_type_constraint(aTHX_ attr->flags & ATTR_MASK_TYPE, attr->tc_check, attr->type_constraint, value) )
764             croak("Bad param");
765     }
766
767     set_slot_value(aTHX_ self, attr, value);
768 }
769
770 /* simple high level api */
771
772 STATIC XS(getter);
773 STATIC XS(getter)
774 {
775 #ifdef dVAR
776     dVAR;
777 #endif
778     dXSARGS;
779     dATTR;
780     SV *value;
781
782     if (items != 1)
783         Perl_croak(aTHX_ "Usage: %s(%s)", GvNAME(CvGV(cv)), "self");
784
785     SP -= items;
786
787     value = getter_common(aTHX_ ST(0), attr);
788
789     if (value) {
790         ST(0) = sv_mortalcopy(value); /* mortalcopy because $_ .= "blah" for $foo->bar */
791         XSRETURN(1);
792     } else {
793         XSRETURN_UNDEF;
794     }
795 }
796
797 STATIC XS(setter);
798 STATIC XS(setter)
799 {
800 #ifdef dVAR
801     dVAR;
802 #endif
803     dXSARGS;
804     dATTR;
805
806     if (items != 2)
807         Perl_croak(aTHX_ "Usage: %s(%s, %s)", GvNAME(CvGV(cv)), "self", "value");
808
809     SP -= items;
810
811     setter_common(aTHX_ ST(0), attr, ST(1));
812
813     ST(0) = ST(1); /* return value */
814     XSRETURN(1);
815 }
816
817 STATIC XS(accessor);
818 STATIC XS(accessor)
819 {
820 #ifdef dVAR
821     dVAR;
822 #endif
823     dXSARGS;
824     dATTR;
825
826     if (items < 1)
827         Perl_croak(aTHX_ "Usage: %s(%s, [ %s ])", GvNAME(CvGV(cv)), "self", "value");
828
829     SP -= items;
830
831     if (items > 1) {
832         setter_common(aTHX_ ST(0), attr, ST(1));
833         ST(0) = ST(1); /* return value */
834     } else {
835         SV *value = getter_common(aTHX_ ST(0), attr);
836         if ( value ) {
837             ST(0) = value;
838         } else {
839             XSRETURN_UNDEF;
840         }
841     }
842
843     XSRETURN(1);
844 }
845
846 STATIC XS(predicate);
847 STATIC XS(predicate)
848 {
849 #ifdef dVAR
850     dVAR;
851 #endif
852     dXSARGS;
853     dATTR;
854
855     if (items != 1)
856         Perl_croak(aTHX_ "Usage: %s(%s)", GvNAME(CvGV(cv)), "self");
857
858     SP -= items;
859
860     if ( has_slot_value(aTHX_ ST(0), attr) )
861         XSRETURN_YES;
862     else
863         XSRETURN_NO;
864 }
865
866 enum xs_body {
867     xs_body_getter = 0,
868     xs_body_setter,
869     xs_body_accessor,
870     xs_body_predicate,
871     max_xs_body
872 };
873
874 STATIC XSPROTO ((*xs_bodies[])) = {
875     getter,
876     setter,
877     accessor,
878     predicate,
879 };
880
881 MODULE = Moose PACKAGE = Moose::XS
882
883 CV *
884 new_sub(attr, name)
885     INPUT:
886         SV *attr;
887         SV *name;
888     ALIAS:
889         new_getter    = xs_body_getter
890         new_setter    = xs_body_setter
891         new_accessor  = xs_body_accessor
892         new_predicate = xs_body_predicate
893     PREINIT:
894         CV * cv;
895     CODE:
896         if ( ix >= max_xs_body )
897             croak("Unknown Moose::XS body type");
898
899         if ( !sv_isobject(attr) )
900             croak("'attr' must be a Moose::Meta::Attribute");
901
902         cv = newXS(SvOK(name) ? SvPV_nolen(name) : NULL, xs_bodies[ix], __FILE__);
903
904         if (cv == NULL)
905             croak("Oi vey!");
906
907         /* associate CV with meta attr */
908         stash_in_mg(aTHX_ (SV *)cv, attr);
909
910         /* this will be set on first call */
911         XSANY.any_i32 = 0;
912
913         RETVAL = cv;
914     OUTPUT:
915         RETVAL
916
917
918 MODULE = Moose  PACKAGE = Moose::XS::Meta::Instance
919
920 void
921 DESTROY(self)
922     INPUT:
923         SV *self;
924     PREINIT:
925         MI *mi = INT2PTR(MI *, SvIV(SvRV(self)));
926     CODE:
927         delete_mi(aTHX_ mi);