Change the mop_instance_vtbl prototypes: they could receive meta instances in the...
[gitmo/Class-MOP.git] / xs / Attribute.xs
1 #include "mop.h"
2
3
4 static MGVTBL mop_attr_vtbl;
5
6 MAGIC*
7 mop_attr_mg(pTHX_ SV* const attr, SV* const instance){
8     MAGIC* mg;
9
10     if(!IsObject(attr)) {
11         croak("Invalid Attribute object");
12     }
13
14     /* attribute mg:
15         mg_obj: meta information (AV*)
16         mg_ptr: meta instance virtual table (mop_instance_vtbl*)
17     */
18
19     if(!(SvMAGICAL(SvRV(attr)) && (mg = mop_mg_find(aTHX_ SvRV(attr), &mop_attr_vtbl, 0))) ) {
20         U16 flags = 0;
21         AV* const meta = newAV();
22         SV* name;
23         SV* sv;
24
25         mg = sv_magicext(SvRV(attr), (SV*)meta, PERL_MAGIC_ext, &mop_attr_vtbl, NULL, 0);
26         SvREFCNT_dec(meta);
27         av_extend(meta, MOP_ATTR_last - 1);
28
29         ENTER;
30         SAVETMPS;
31
32         name = mop_call0(aTHX_ attr, mop_name);
33         av_store(meta, MOP_ATTR_SLOT, newSVsv_share(name));
34
35         if(SvOK( sv = mop_call0_pvs(attr, "init_arg") )) {
36             flags |= MOP_ATTRf_HAS_INIT_ARG;
37
38             av_store(meta, MOP_ATTR_INIT_ARG, newSVsv_share(sv));
39         }
40
41         /* NOTE: Setting both default and builder is not allowed */
42         if(SvOK( sv = mop_call0_pvs(attr, "builder") )) {
43             SV* const builder = sv;
44             flags |= MOP_ATTRf_HAS_BUILDER;
45
46             if(SvOK( sv = mop_call1(aTHX_ instance, mop_can, builder) )){
47                 av_store(meta, MOP_ATTR_BUILDER, newSVsv(sv));
48             }
49             else{
50                 croak("%s does not support builder method '%"SVf"' for attribute '%"SVf"'",
51                     sv_reftype(SvRV(instance), TRUE), builder, name);
52             }
53         }
54         else if(SvOK( sv = mop_call0_pvs(attr, "default") )) {
55             if(SvTRUEx( mop_call0_pvs(attr, "is_default_a_coderef") )){
56                 flags |= MOP_ATTRf_HAS_BUILDER;
57                 av_store(meta, MOP_ATTR_BUILDER, newSVsv(sv));
58             }
59             else {
60                 flags |= MOP_ATTRf_HAS_DEFAULT;
61                 av_store(meta, MOP_ATTR_DEFAULT, newSVsv(sv));
62             }
63         }
64
65         MOP_mg_flags(mg) = flags;
66
67         if(flags & MOP_ATTRf_DEBUG) {
68             warn("%s: setup attr_mg for '%"SVf"'\n", sv_reftype(SvRV(instance), TRUE), name);
69         }
70
71         FREETMPS;
72         LEAVE;
73     }
74
75     return mg;
76 }
77
78 void
79 mop_attr_initialize_instance_slot(pTHX_ SV* const attr, const mop_instance_vtbl* const vtbl, SV* const instance, HV* const args){
80     MAGIC* const mg  = mop_attr_mg(aTHX_ attr, instance);
81     AV* const meta   = (AV*)MOP_mg_obj(mg);
82     U16 const flags  = MOP_mg_flags(mg);
83     HE* arg;
84     SV* value;
85
86     if(flags & MOP_ATTRf_DEBUG){
87         warn("%s: initialize_instance_slot '%"SVf"' (0x%04x)\n", sv_reftype(SvRV(instance), TRUE), MOP_attr_slot(meta), (unsigned)flags);
88     }
89
90     if( flags & MOP_ATTRf_HAS_INIT_ARG && (arg = hv_fetch_ent(args, MOP_attr_init_arg(meta), FALSE, 0U)) ){
91         value = hv_iterval(args, arg);
92     }
93     else if(flags & MOP_ATTRf_HAS_DEFAULT) {
94         value = MOP_attr_default(meta); /* it's always a non-ref value */
95     }
96     else if(flags & MOP_ATTRf_HAS_BUILDER) {
97         SV* const builder = MOP_attr_builder(meta); /* code-ref default value or builder */
98         dSP;
99
100         ENTER;
101         SAVETMPS;
102
103         PUSHMARK(SP);
104         XPUSHs(instance);
105         PUTBACK;
106
107         call_sv(builder, G_SCALAR);
108
109         SPAGAIN;
110         value = POPs;
111         SvREFCNT_inc_simple_void_NN(value);
112         PUTBACK;
113
114         FREETMPS;
115         LEAVE;
116
117         sv_2mortal(value);
118     }
119     else{
120         value = NULL;
121     }
122
123     if(value){
124         if(flags & MOP_ATTRf_HAS_INITIALIZER){
125             /* $attr->set_initial_value($meta_instance, $instance, $value) */
126             dSP;
127
128             PUSHMARK(SP);
129             EXTEND(SP, 4);
130             PUSHs(attr);
131             PUSHs(instance);
132             mPUSHs(value);
133             PUTBACK;
134
135             call_method("set_initial_value", G_VOID | G_DISCARD);
136         }
137         else{
138             vtbl->set_slot(aTHX_ NULL, instance, MOP_attr_slot(meta), value);
139         }
140     }
141 }
142
143
144 MODULE = Class::MOP::Attribute   PACKAGE = Class::MOP::Attribute
145
146 PROTOTYPES: DISABLE
147
148 VERSIONCHECK: DISABLE
149
150 BOOT:
151     INSTALL_SIMPLE_READER(Attribute, name);
152     INSTALL_SIMPLE_READER(Attribute, associated_class);
153     INSTALL_SIMPLE_READER(Attribute, associated_methods);
154     INSTALL_SIMPLE_READER(Attribute, accessor);
155     INSTALL_SIMPLE_READER(Attribute, reader);
156     INSTALL_SIMPLE_READER(Attribute, writer);
157     INSTALL_SIMPLE_READER(Attribute, predicate);
158     INSTALL_SIMPLE_READER(Attribute, clearer);
159     INSTALL_SIMPLE_READER(Attribute, builder);
160     INSTALL_SIMPLE_READER(Attribute, init_arg);
161     INSTALL_SIMPLE_READER(Attribute, initializer);
162     INSTALL_SIMPLE_READER(Attribute, insertion_order);
163     INSTALL_SIMPLE_READER(Attribute, definition_context);
164
165     INSTALL_SIMPLE_WRITER_WITH_KEY(Attribute, _set_insertion_order, insertion_order);
166
167     INSTALL_SIMPLE_PREDICATE(Attribute, accessor);
168     INSTALL_SIMPLE_PREDICATE(Attribute, reader);
169     INSTALL_SIMPLE_PREDICATE(Attribute, writer);
170     INSTALL_SIMPLE_PREDICATE(Attribute, predicate);
171     INSTALL_SIMPLE_PREDICATE(Attribute, clearer);
172     INSTALL_SIMPLE_PREDICATE(Attribute, builder);
173     INSTALL_SIMPLE_PREDICATE(Attribute, init_arg);
174     INSTALL_SIMPLE_PREDICATE(Attribute, initializer);
175     INSTALL_SIMPLE_PREDICATE(Attribute, default);
176