FIXMEs no longer required
[gitmo/Moose.git] / Moose.xs
CommitLineData
1ea12c91 1#include "EXTERN.h"
2#include "perl.h"
3#include "XSUB.h"
4
035fd0c4 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
1ea12c91 18/* FIXME
1ea12c91 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
de2f2e97 31 * for a constructor we have ATTR *attrs, and iterate that, removing init_arg
1ea12c91 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 {
de2f2e97 40 * ATTR *attr;
1ea12c91 41 * pv *method;
42 * } delegation;
43 *
44 * typedef struct {
de2f2e97 45 * ATTR *attr;
1ea12c91 46 * I32 *type; // hash, array, whatever + vtable for operation
47 * } attributehelper;
48 */
49
de2f2e97 50
51STATIC 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
f253044f 68STATIC 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
75STATIC 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}
de2f2e97 89
90
91typedef enum {
92 Any = 0,
93 Item,
94 Bool,
95 Maybe, /* [`a] */
96 Undef,
97 Defined,
98 Value,
99 Num,
100 Int,
101 Str,
102 ClassName,
103 Ref,
104 ScalarRef,
105 ArrayRef, /* [`a] */
106 HashRef, /* [`a] */
107 CodeRef,
108 RegexpRef,
109 GlobRef,
110 FileHandle,
111 Object,
112 Role,
113
114 /* XS only types */
115 Class,
116
117 max_TC
118} TC;
119
120typedef union {
121 TC type;
122 CV *cv;
123 HV *stash;
124 OP *op;
125} TC_CHECK;
126
127typedef enum {
128 tc_none = 0,
129 tc_type,
130 tc_cv,
131 tc_stash,
132 tc_op,
133} tc_kind;
134
135typedef union {
136 char *builder;
137 SV *value;
138 CV *sub;
139 OP *op;
140 U32 type;
141} DEFAULT;
142
143typedef enum {
144 default_none = 0,
145 default_type,
146 default_builder,
147 default_value,
148 default_sub,
149 default_op,
150} default_kind;
151
1ea12c91 152typedef struct {
de2f2e97 153 /* the meta instance struct */
154 struct mi *mi;
155
156 U32 flags; /* slot type, TC behavior, coerce, weaken, (no default | default, builder + lazy), auto_deref */
157
158 /* slot access fields */
159 SV *slot_sv; /* value of the slot (slot name presumably) */
160 U32 slot_u32; /* for optimized access (precomputed hash or otherr) */
161
162 DEFAULT def; /* cv, value or other, depending on flags */
163
164 TC_CHECK tc_check; /* cv, value or other, dependidng on flags */
165 SV *type_constraint; /* meta attr */
166
167 CV *initializer;
168 CV *trigger;
169
f253044f 170 SV *meta_attr; /* the meta attr object */
de2f2e97 171 AV *cvs; /* CVs which use this attr */
172} ATTR;
173
174/* slot flags:
175 * instance reading writing
176 * 00000000 00000000 00000000 00000000
177 * ^ trigger
178 * ^ weak
179 * ^^^ tc_kind
180 * ^ coerce
181 * ^^^ default_kind
182 * ^ lazy
183 * ^ required
184 * ^^^^^^^ if 0 then nothing special (just hash)? FIXME TBD
185 */
186
187#define ATTR_INSTANCE_MASK 0xff000000
188#define ATTR_READING_MASK 0x0000ff00
189#define ATTR_WRITING_MASK 0x000000ff
190
191#define ATTR_MASK_TYPE 0x7
192
193#define ATTR_MASK_DEFAULT 0x700
194#define ATTR_SHIFT_DEAFULT 8
195
196#define ATTR_LAZY 0x800
197
198#define ATTR_COERCE 0x08
199#define ATTR_WEAK 0x10
200#define ATTR_TRIGGER 0x10
201
202#define ATTR_ISWEAK(attr) ( attr->flags & ATTR_WEAK )
203#define ATTR_ISLAZY(attr) ( attr->flags & ATTR_LAZY )
204#define ATTR_ISCOERCE(attr) ( attr->flags & ATTR_COERCE )
1ea12c91 205
de2f2e97 206#define ATTR_TYPE(f) ( attr->flags & 0x7 )
207#define ATTR_DEFAULT(f) ( ( attr->flags & ATTR_MASK_DEFAULT ) >> ATTR_SHIFT_DEFAULT )
1ea12c91 208
de2f2e97 209#define ATTR_DUMB_READER(attr) !ATTR_IS_LAZY(attr)
210#define ATTR_DUMB_WRITER(attr) ( ( attr->flags & ATTR_WRITING_MASK ) == 0 )
211#define ATTR_DUMB_INSTANCE(attr) ( ( attr->flags & ATTR_INSTANCE_MASK ) == 0 )
1ea12c91 212
f253044f 213#define dATTR ATTR *attr = (XSANY.any_i32 ? INT2PTR(ATTR *, (XSANY.any_i32)) : define_attr(aTHX_ cv))
de2f2e97 214
215
216/* FIXME define a vtable that does call_sv */
217typedef struct {
218 SV * (*get)(pTHX_ SV *self, ATTR *attr);
219 void (*set)(pTHX_ SV *self, ATTR *attr, SV *value);
220 bool * (*has)(pTHX_ SV *self, ATTR *attr);
221 SV * (*delete)(pTHX_ SV *self, ATTR *attr);
222} instance_vtbl;
223
224
225typedef enum {
226 hash = 0,
227
228 /* these are not yet implemented */
229 array,
230 fptr,
231 cv,
232 judy,
233} instance_types;
234
235typedef struct mi {
de2f2e97 236 HV *stash;
237
238 /* slot access method */
239 instance_types type;
240 instance_vtbl *vtbl;
241
242 /* attr descriptors */
243 I32 num_attrs;
244 ATTR *attrs;
245} MI;
246
247
248STATIC void init_attr (MI *mi, ATTR *attr, HV *desc) {
1ea12c91 249 U32 hash;
250 STRLEN len;
de2f2e97 251 SV **key = hv_fetchs(desc, "key", 0);
252 SV **meta_attr = hv_fetchs(desc, "meta", 0);
253 char *pv;
254
255 if ( !meta_attr ) croak("'meta' is required");
256
035fd0c4 257 attr->meta_attr = newSVsv(*meta_attr);
de2f2e97 258
259 attr->mi = mi;
260
261 attr->flags = 0;
262
263
264 /* if type == hash */
265 /* prehash the key */
266 if ( !key ) croak("'key' is required");
267
268 pv = SvPV(*key, len);
1ea12c91 269
270 PERL_HASH(hash, pv, len);
1ea12c91 271
de2f2e97 272 attr->slot_sv = newSVpvn_share(pv, len, hash);
273 attr->slot_u32 = hash;
274
275 attr->def.type = 0;
276
277 attr->tc_check.type = 0;
278 attr->type_constraint = NULL;
279
280
281 attr->initializer = NULL;
282 attr->trigger = NULL;
283
284 /* cross refs to CVs which use this struct */
285 attr->cvs = newAV();
286}
287
f253044f 288STATIC MI *new_mi (pTHX_ HV *stash, AV *attrs) {
de2f2e97 289 MI *mi;
290 I32 ix;
291 const I32 num = av_len(attrs) + 1;
292
293 Newx(mi, 1, MI);
294
295 SvREFCNT_inc_simple(stash);
296 mi->stash = stash;
297
de2f2e97 298 mi->type = 0; /* nothing else implemented yet */
299
300 /* initialize attributes */
301 mi->num_attrs = num;
302 Newx(mi->attrs, num, ATTR);
f253044f 303 for ( ix = 0; ix < num; ix++ ) {
de2f2e97 304 SV **desc = av_fetch(attrs, ix, 0);
305
f253044f 306 if ( !desc || !*desc || !SvROK(*desc) || !(SvTYPE(SvRV(*desc)) == SVt_PVHV) ) {
de2f2e97 307 croak("Attribute descriptor has to be a hash reference");
f253044f 308 }
de2f2e97 309
310 init_attr(mi, &mi->attrs[ix], (HV *)SvRV(*desc));
311 }
312
313 return mi;
314}
315
f253044f 316STATIC SV *new_mi_obj (pTHX_ MI *mi) {
035fd0c4 317 HV *stash = gv_stashpvs("Moose::XS::Meta::Instance",0);
318 SV *obj = newRV_noinc(newSViv(PTR2IV(mi)));
319 sv_bless( obj, stash );
320 return obj;
f253044f 321}
de2f2e97 322
f253044f 323STATIC SV *attr_to_meta_instance(pTHX_ SV *meta_attr) {
324 dSP;
325 I32 count;
326 SV *mi;
327
328 if ( !meta_attr )
329 croak("No attr found in magic!");
330
331 ENTER;
332 SAVETMPS;
333 PUSHMARK(SP);
334 XPUSHs(meta_attr);
335 PUTBACK;
336 count = call_pv("Moose::XS::attr_to_meta_instance", G_SCALAR);
337
338 if ( count != 1 )
339 croak("attr_to_meta_instance borked (%d args returned, expecting 1)", count);
340
341 SPAGAIN;
342 mi = POPs;
343
344 SvREFCNT_inc(mi);
345
346 PUTBACK;
347 FREETMPS;
348 LEAVE;
349
350 return mi;
351}
352
353STATIC SV *perl_mi_to_c_mi(pTHX_ SV *perl_mi) {
354 dSP;
355 I32 count;
356 MI *mi = NULL;
357 SV *class;
358 SV *attrs;
359 HV *stash;
360
361 ENTER;
362 SAVETMPS;
363 PUSHMARK(SP);
364 XPUSHs(perl_mi);
365 PUTBACK;
366 count = call_pv("Moose::XS::meta_instance_to_attr_descs", G_ARRAY);
367
368 if ( count != 2 )
369 croak("meta_instance_to_attr_descs borked (%d args returned, expecting 2)", count);
370
371 SPAGAIN;
372 attrs = POPs;
373 class = POPs;
374
375 PUTBACK;
376
377 stash = gv_stashsv(class, 0);
378
379 mi = new_mi(aTHX_ stash, (AV *)SvRV(attrs));
380
381 FREETMPS;
382 LEAVE;
383
384 return new_mi_obj(aTHX_ mi);
385}
386
387STATIC ATTR *mi_find_attr(MI *mi, SV *meta_attr) {
388 I32 ix;
389
035fd0c4 390 for ( ix = 0; ix < mi->num_attrs; ix++ ) {
f253044f 391 if ( SvRV(mi->attrs[ix].meta_attr) == SvRV(meta_attr) ) {
392 return &mi->attrs[ix];
de2f2e97 393 }
de2f2e97 394 }
395
f253044f 396 sv_dump(meta_attr);
397 croak("Attr not found");
de2f2e97 398 return NULL;
399}
400
401STATIC ATTR *get_attr(pTHX_ CV *cv) {
f253044f 402 SV *meta_attr = get_stashed_in_mg(aTHX_ (SV *)cv);
403 SV *perl_mi = attr_to_meta_instance(aTHX_ meta_attr);
404 SV *c_mi = get_stashed_in_mg(aTHX_ SvRV(perl_mi));
405 MI *mi;
de2f2e97 406
f253044f 407 if (!c_mi) {
408 c_mi = perl_mi_to_c_mi(aTHX_ perl_mi);
409 stash_in_mg(aTHX_ perl_mi, c_mi);
410 }
411
412 sv_2mortal(perl_mi);
413
414 mi = INT2PTR(MI *, SvIV(SvRV(c_mi)));
415
416 return mi_find_attr(mi, meta_attr);
1ea12c91 417}
418
de2f2e97 419STATIC ATTR *define_attr (pTHX_ CV *cv) {
420 ATTR *attr = get_attr(aTHX_ cv);
421 assert(attr);
422
423 XSANY.any_i32 = PTR2IV(attr);
f253044f 424
425 av_push( attr->cvs, (SV *)cv );
de2f2e97 426
427 return attr;
428}
429
430STATIC void weaken(pTHX_ SV *sv) {
1ea12c91 431#ifdef SvWEAKREF
de2f2e97 432 sv_rvweaken(sv); /* FIXME i think this might warn when weakening an already weak ref */
1ea12c91 433#else
434 croak("weak references are not implemented in this release of perl");
435#endif
436}
437
438
439/* meta instance protocol */
440
de2f2e97 441STATIC SV *get_slot_value(pTHX_ SV *self, ATTR *attr) {
1ea12c91 442 HE *he;
443
444 assert(self);
445 assert(SvROK(self));
446 assert(SvTYPE(SvRV(self)) == SVt_PVHV);
447
de2f2e97 448 assert( ATTR_DUMB_INSTANCE(attr) );
449
450 if ((he = hv_fetch_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32)))
1ea12c91 451 return HeVAL(he);
452 else
453 return NULL;
454}
455
de2f2e97 456STATIC void set_slot_value(pTHX_ SV *self, ATTR *attr, SV *value) {
1ea12c91 457 HE *he;
458
459 assert(self);
460 assert(SvROK(self));
461 assert(SvTYPE(SvRV(self)) == SVt_PVHV);
462
de2f2e97 463 assert( ATTR_DUMB_INSTANCE(attr) );
464
1ea12c91 465 SvREFCNT_inc(value);
466
de2f2e97 467 he = hv_store_ent((HV*)SvRV(self), attr->slot_sv, value, attr->slot_u32);
1ea12c91 468 if (he != NULL) {
de2f2e97 469 if ( ATTR_ISWEAK(attr) )
470 weaken(aTHX_ HeVAL(he)); /* actually only needed once at HE creation time */
1ea12c91 471 } else {
472 croak("Hash store failed.");
473 }
474}
475
de2f2e97 476STATIC bool has_slot_value(pTHX_ SV *self, ATTR *attr) {
1ea12c91 477 assert(self);
478 assert(SvROK(self));
479 assert(SvTYPE(SvRV(self)) == SVt_PVHV);
480
de2f2e97 481 assert( ATTR_DUMB_INSTANCE(attr) );
482
483 return hv_exists_ent((HV *)SvRV(self), attr->slot_sv, attr->slot_u32);
484}
485
486STATIC SV *deinitialize_slot(pTHX_ SV *self, ATTR *attr) {
487 assert(self);
488 assert(SvROK(self));
489 assert(SvTYPE(SvRV(self)) == SVt_PVHV);
490
491 assert( ATTR_DUMB_INSTANCE(attr) );
492
493 return hv_delete_ent((HV *)SvRV(self), attr->slot_sv, 0, attr->slot_u32);
1ea12c91 494}
495
496
497/* simple high level api */
498
de2f2e97 499STATIC XS(getter);
500STATIC XS(getter)
1ea12c91 501{
502#ifdef dVAR
503 dVAR;
504#endif
505 dXSARGS;
de2f2e97 506 dATTR;
1ea12c91 507 SV *value;
508
509 if (items != 1)
510 Perl_croak(aTHX_ "Usage: %s(%s)", GvNAME(CvGV(cv)), "self");
511
512 SP -= items;
513
de2f2e97 514 assert( ATTR_DUMB_READER(attr) );
515
516 value = get_slot_value(aTHX_ ST(0), attr);
1ea12c91 517
518 if (value) {
519 ST(0) = sv_mortalcopy(value); /* mortalcopy because $_ .= "blah" for $foo->bar */
520 XSRETURN(1);
521 } else {
522 XSRETURN_UNDEF;
523 }
524}
525
de2f2e97 526STATIC XS(setter);
527STATIC XS(setter)
1ea12c91 528{
529#ifdef dVAR
530 dVAR;
531#endif
532 dXSARGS;
de2f2e97 533 dATTR;
1ea12c91 534
535 if (items != 2)
536 Perl_croak(aTHX_ "Usage: %s(%s, %s)", GvNAME(CvGV(cv)), "self", "value");
537
538 SP -= items;
539
de2f2e97 540 assert( ATTR_DUMB_WRITER(attr) );
541
542 set_slot_value(aTHX_ ST(0), attr, ST(1));
1ea12c91 543
544 ST(0) = ST(1); /* return value */
545 XSRETURN(1);
546}
547
de2f2e97 548STATIC XS(accessor);
549STATIC XS(accessor)
1ea12c91 550{
551#ifdef dVAR
552 dVAR;
553#endif
554 dXSARGS;
de2f2e97 555 dATTR;
1ea12c91 556
557 if (items < 1)
558 Perl_croak(aTHX_ "Usage: %s(%s, [ %s ])", GvNAME(CvGV(cv)), "self", "value");
559
560 SP -= items;
561
562 if (items > 1) {
de2f2e97 563 assert( ATTR_DUMB_READER(attr) );
564 set_slot_value(aTHX_ ST(0), attr, ST(1));
1ea12c91 565 ST(0) = ST(1); /* return value */
566 } else {
035fd0c4 567 SV *value;
de2f2e97 568 assert( ATTR_DUMB_WRITER(attr) );
035fd0c4 569 value = get_slot_value(aTHX_ ST(0), attr);
1ea12c91 570 if ( value ) {
571 ST(0) = value;
572 } else {
573 XSRETURN_UNDEF;
574 }
575 }
576
577 XSRETURN(1);
578}
579
580STATIC XS(predicate);
581STATIC XS(predicate)
582{
583#ifdef dVAR
584 dVAR;
585#endif
586 dXSARGS;
de2f2e97 587 dATTR;
1ea12c91 588
589 if (items != 1)
590 Perl_croak(aTHX_ "Usage: %s(%s)", GvNAME(CvGV(cv)), "self");
591
592 SP -= items;
593
de2f2e97 594 if ( has_slot_value(aTHX_ ST(0), attr) )
1ea12c91 595 XSRETURN_YES;
596 else
597 XSRETURN_NO;
598}
599
600enum xs_body {
de2f2e97 601 xs_body_getter = 0,
602 xs_body_setter,
603 xs_body_accessor,
1ea12c91 604 xs_body_predicate,
605 max_xs_body
606};
607
608STATIC XSPROTO ((*xs_bodies[])) = {
de2f2e97 609 getter,
610 setter,
611 accessor,
1ea12c91 612 predicate,
613};
614
615MODULE = Moose PACKAGE = Moose::XS
616
617CV *
de2f2e97 618new_sub(attr, name)
1ea12c91 619 INPUT:
de2f2e97 620 SV *attr;
621 SV *name;
1ea12c91 622 ALIAS:
de2f2e97 623 new_getter = xs_body_getter
624 new_setter = xs_body_setter
625 new_accessor = xs_body_accessor
626 new_predicate = xs_body_predicate
1ea12c91 627 PREINIT:
628 CV * cv;
629 CODE:
630 if ( ix >= max_xs_body )
631 croak("Unknown Moose::XS body type");
632
de2f2e97 633 if ( !sv_isobject(attr) )
634 croak("'attr' must be a Moose::Meta::Attribute");
635
636 cv = newXS(SvOK(name) ? SvPV_nolen(name) : NULL, xs_bodies[ix], __FILE__);
1ea12c91 637
638 if (cv == NULL)
639 croak("Oi vey!");
640
de2f2e97 641 /* associate CV with meta attr */
f253044f 642 stash_in_mg(aTHX_ (SV *)cv, attr);
de2f2e97 643
644 /* this will be set on first call */
645 XSANY.any_i32 = 0;
1ea12c91 646
647 RETVAL = cv;
648 OUTPUT:
649 RETVAL
650
651
f253044f 652MODULE = Moose PACKAGE = Moose::XS::Meta::Instance
653
654void
655DESTROY(self)
656 INPUT:
657 SV *self;
658 PREINIT:
659 MI *mi = INT2PTR(MI *, SvIV(SvRV(self)));
660 CODE:
661 /* foreach attr ( delete cvs XSANY ), free attrs free mi */