4 static MGVTBL mop_attr_vtbl;
6 #define MOP_attr_slot(meta) MOP_av_at(meta, MOP_ATTR_SLOT)
7 #define MOP_attr_init_arg(meta) MOP_av_at(meta, MOP_ATTR_INIT_ARG)
8 #define MOP_attr_default(meta) MOP_av_at(meta, MOP_ATTR_DEFAULT)
9 #define MOP_attr_builder(meta) MOP_av_at(meta, MOP_ATTR_BUILDER)
21 enum mop_attr_flags_t{ /* must be 16 bits */
22 MOP_ATTRf_HAS_INIT_ARG = 0x0001,
23 MOP_ATTRf_HAS_DEFAULT = 0x0002,
24 MOP_ATTRf_IS_DEFAULT_A_CODEREF = 0x0004,
25 MOP_ATTRf_HAS_BUILDER = 0x0008,
26 MOP_ATTRf_HAS_INITIALIZER = 0x0010,
29 MOP_ATTRf_DEBUG = 0x8000
33 mop_attr_mg(pTHX_ SV* const attr, SV* const instance){
37 croak("Invalid Attribute object");
41 mg_obj: meta information (AV*)
42 mg_ptr: meta instance virtual table (mop_instance_vtbl*)
45 if(!(SvMAGICAL(SvRV(attr)) && (mg = mop_mg_find(aTHX_ SvRV(attr), &mop_attr_vtbl, 0))) ) {
47 AV* const meta = newAV();
51 mg = sv_magicext(SvRV(attr), (SV*)meta, PERL_MAGIC_ext, &mop_attr_vtbl, NULL, 0);
53 av_extend(meta, MOP_ATTR_last - 1);
58 name = mop_call0(aTHX_ attr, mop_name);
59 av_store(meta, MOP_ATTR_SLOT, newSVsv_share(name));
61 if(SvOK( sv = mop_call0_pvs(attr, "init_arg") )) {
62 flags |= MOP_ATTRf_HAS_INIT_ARG;
64 av_store(meta, MOP_ATTR_INIT_ARG, newSVsv_share(sv));
67 /* NOTE: Setting both default and builder is not allowed */
68 if(SvOK( sv = mop_call0_pvs(attr, "builder") )) {
69 SV* const builder = sv;
70 flags |= MOP_ATTRf_HAS_BUILDER;
72 if(SvOK( sv = mop_call1(aTHX_ instance, mop_can, builder) )){
73 av_store(meta, MOP_ATTR_BUILDER, newSVsv(sv));
76 croak("%s does not support builder method '%"SVf"' for attribute '%"SVf"'",
77 sv_reftype(SvRV(instance), TRUE), builder, name);
80 else if(SvOK( sv = mop_call0_pvs(attr, "default") )) {
81 if(SvTRUEx( mop_call0_pvs(attr, "is_default_a_coderef") )){
82 flags |= MOP_ATTRf_HAS_BUILDER;
83 av_store(meta, MOP_ATTR_BUILDER, newSVsv(sv));
86 flags |= MOP_ATTRf_HAS_DEFAULT;
87 av_store(meta, MOP_ATTR_DEFAULT, newSVsv(sv));
91 MOP_mg_flags(mg) = flags;
93 if(flags & MOP_ATTRf_DEBUG) {
94 warn("%s: setup attr_mg for '%"SVf"'\n", sv_reftype(SvRV(instance), TRUE), name);
104 static MGVTBL mop_constructor_vtbl;
107 mop_build_args(pTHX_ CV* const cv, I32 const ax, I32 const items){
110 SV* const sv = ST(0);
112 if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)){
113 croak("Single arguments to %s() must be a HASH ref", GvNAME(CvGV(cv)));
115 args = (HV*)SvRV(sv);
121 croak("Odd number of arguments for %s()", GvNAME(CvGV(cv)));
125 sv_2mortal((SV*)args);
127 for(i = 0; i < items; i += 2){
128 SV* const key = ST(i);
129 SV* const value = ST(i+1);
130 (void)hv_store_ent(args, key, value, 0U);
131 SvREFCNT_inc_simple_void_NN(value);
138 mop_attr_initialize_instance_slot(pTHX_ SV* const attr, const mop_instance_vtbl* const vtbl, SV* const instance, HV* const args){
139 MAGIC* const mg = mop_attr_mg(aTHX_ attr, instance);
140 AV* const meta = (AV*)MOP_mg_obj(mg);
141 U16 const flags = MOP_mg_flags(mg);
145 if(flags & MOP_ATTRf_DEBUG){
146 warn("%s: initialize_instance_slot '%"SVf"' (0x%04x)\n", sv_reftype(SvRV(instance), TRUE), MOP_attr_slot(meta), (unsigned)flags);
149 if( flags & MOP_ATTRf_HAS_INIT_ARG && (arg = hv_fetch_ent(args, MOP_attr_init_arg(meta), FALSE, 0U)) ){
150 value = hv_iterval(args, arg);
152 else if(flags & MOP_ATTRf_HAS_DEFAULT) {
153 value = MOP_attr_default(meta); /* it's always a non-ref value */
155 else if(flags & MOP_ATTRf_HAS_BUILDER) {
156 SV* const builder = MOP_attr_builder(meta); /* code-ref default value or builder */
166 call_sv(builder, G_SCALAR);
170 SvREFCNT_inc_simple_void_NN(value);
183 if(flags & MOP_ATTRf_HAS_INITIALIZER){
184 /* $attr->set_initial_value($meta_instance, $instance, $value) */
194 call_method("set_initial_value", G_VOID | G_DISCARD);
197 vtbl->set_slot(aTHX_ instance, MOP_attr_slot(meta), value);
203 mop_class_get_all_attributes(pTHX_ SV* const metaclass){
204 AV* const attrs = newAV();
212 n = call_method("get_all_attributes", G_ARRAY);
216 av_extend(attrs, n - 1);
218 (void)av_store(attrs, --n, newSVsv(POPs));
227 XS(mop_xs_constructor);
228 XS(mop_xs_constructor)
232 AV* const attrs = (AV*)MOP_mg_obj(mg);
240 assert(SvTYPE(attrs) == SVt_PVAV);
243 croak("Not enough arguments for %s()", GvNAME(CvGV(cv)));
249 croak("The constructor must be called as a class method");
252 stash = gv_stashsv(klass, TRUE);
254 args = mop_build_args(aTHX_ cv, ax+1, items-1);
256 if( stash != GvSTASH(CvGV(cv)) ){
257 SV* const metaclass = mop_class_of(aTHX_ klass);
263 mPUSHs(newRV_inc((SV*)args));
266 call_method("new_object", GIMME_V);
270 instance = sv_2mortal( MOP_mg_create_instance(mg, stash) );
271 if(!IsObject(instance)){
272 croak("create_instance() did not return an object instance");
275 len = AvFILLp(attrs) + 1;
276 for(i = 0; i < len; i++){
277 mop_attr_initialize_instance_slot(aTHX_ AvARRAY(attrs)[i], MOP_mg_vtbl(mg), instance, args);
286 mop_generate_constructor_method_xs(pTHX_ SV* const constructor, mop_instance_vtbl* const vtbl){
287 SV* const metaclass = mop_call0(aTHX_ constructor, mop_associated_metaclass);
289 CV* const xsub = newXS(NULL, mop_xs_constructor, __FILE__);
293 sv_2mortal((SV*)xsub);
295 attrs = mop_class_get_all_attributes(aTHX_ metaclass);
296 mg = sv_magicext((SV*)xsub, (SV*)attrs, PERL_MAGIC_ext, &mop_constructor_vtbl, (char*)vtbl, 0);
298 CvXSUBANY(xsub).any_ptr = (void*)mg;
304 MODULE = Class::MOP::Method::Constructor PACKAGE = Class::MOP::Method::Constructor
308 VERSIONCHECK: DISABLE
311 INSTALL_SIMPLE_READER(Method::Constructor, options);
312 INSTALL_SIMPLE_READER(Method::Constructor, associated_metaclass);
315 _generate_constructor_method_xs(SV* self, void* instance_vtbl)
317 RETVAL = mop_generate_constructor_method_xs(aTHX_ self, instance_vtbl);