Commit | Line | Data |
87cfe982 |
1 | #include "mop.h" |
2 | |
3 | |
87cfe982 |
4 | static MGVTBL mop_constructor_vtbl; |
5 | |
6 | static HV* |
7 | mop_build_args(pTHX_ CV* const cv, I32 const ax, I32 const items){ |
8 | HV* args; |
9 | if(items == 1){ |
10 | SV* const sv = ST(0); |
11 | SvGETMAGIC(sv); |
12 | if(!(SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)){ |
13 | croak("Single arguments to %s() must be a HASH ref", GvNAME(CvGV(cv))); |
14 | } |
15 | args = (HV*)SvRV(sv); |
16 | } |
17 | else{ |
18 | I32 i; |
19 | |
20 | if( items % 2 ){ |
21 | croak("Odd number of arguments for %s()", GvNAME(CvGV(cv))); |
22 | } |
23 | |
24 | args = newHV(); |
25 | sv_2mortal((SV*)args); |
26 | |
27 | for(i = 0; i < items; i += 2){ |
28 | SV* const key = ST(i); |
29 | SV* const value = ST(i+1); |
30 | (void)hv_store_ent(args, key, value, 0U); |
31 | SvREFCNT_inc_simple_void_NN(value); |
32 | } |
33 | } |
34 | return args; |
35 | } |
36 | |
87cfe982 |
37 | |
38 | XS(mop_xs_constructor); |
39 | XS(mop_xs_constructor) |
40 | { |
41 | dVAR; dXSARGS; |
42 | dMOP_mg(cv); |
43 | AV* const attrs = (AV*)MOP_mg_obj(mg); |
44 | SV* klass; |
45 | HV* stash; |
46 | SV* instance; |
47 | I32 i; |
48 | I32 len; |
49 | HV* args; |
50 | |
51 | assert(SvTYPE(attrs) == SVt_PVAV); |
52 | |
53 | if(items < 0){ |
54 | croak("Not enough arguments for %s()", GvNAME(CvGV(cv))); |
55 | } |
56 | |
2b20ec6a |
57 | SP -= items; |
58 | PUTBACK; |
59 | |
87cfe982 |
60 | klass = ST(0); |
61 | |
62 | if(SvROK(klass)){ |
63 | croak("The constructor must be called as a class method"); |
64 | } |
65 | |
87cfe982 |
66 | args = mop_build_args(aTHX_ cv, ax+1, items-1); |
67 | |
5bd0f967 |
68 | stash = gv_stashsv(klass, TRUE); |
87cfe982 |
69 | if( stash != GvSTASH(CvGV(cv)) ){ |
70 | SV* const metaclass = mop_class_of(aTHX_ klass); |
71 | dSP; |
72 | |
73 | PUSHMARK(SP); |
74 | EXTEND(SP, 2); |
75 | PUSHs(metaclass); |
76 | mPUSHs(newRV_inc((SV*)args)); |
77 | PUTBACK; |
78 | |
79 | call_method("new_object", GIMME_V); |
80 | return; |
81 | } |
82 | |
83 | instance = sv_2mortal( MOP_mg_create_instance(mg, stash) ); |
84 | if(!IsObject(instance)){ |
85 | croak("create_instance() did not return an object instance"); |
86 | } |
87 | |
88 | len = AvFILLp(attrs) + 1; |
89 | for(i = 0; i < len; i++){ |
90 | mop_attr_initialize_instance_slot(aTHX_ AvARRAY(attrs)[i], MOP_mg_vtbl(mg), instance, args); |
91 | } |
92 | |
93 | ST(0) = instance; |
94 | XSRETURN(1); |
95 | } |
96 | |
97 | |
98 | static CV* |
99 | mop_generate_constructor_method_xs(pTHX_ SV* const constructor, mop_instance_vtbl* const vtbl){ |
100 | SV* const metaclass = mop_call0(aTHX_ constructor, mop_associated_metaclass); |
101 | |
102 | CV* const xsub = newXS(NULL, mop_xs_constructor, __FILE__); |
103 | MAGIC* mg; |
104 | AV* attrs; |
105 | |
106 | sv_2mortal((SV*)xsub); |
107 | |
108 | attrs = mop_class_get_all_attributes(aTHX_ metaclass); |
109 | mg = sv_magicext((SV*)xsub, (SV*)attrs, PERL_MAGIC_ext, &mop_constructor_vtbl, (char*)vtbl, 0); |
110 | SvREFCNT_dec(attrs); |
111 | CvXSUBANY(xsub).any_ptr = (void*)mg; |
112 | |
113 | return xsub; |
114 | } |
115 | |
116 | |
117 | MODULE = Class::MOP::Method::Constructor PACKAGE = Class::MOP::Method::Constructor |
118 | |
119 | PROTOTYPES: DISABLE |
120 | |
b66ddbab |
121 | VERSIONCHECK: DISABLE |
122 | |
87cfe982 |
123 | BOOT: |
124 | INSTALL_SIMPLE_READER(Method::Constructor, options); |
125 | INSTALL_SIMPLE_READER(Method::Constructor, associated_metaclass); |
126 | |
127 | CV* |
128 | _generate_constructor_method_xs(SV* self, void* instance_vtbl) |
129 | CODE: |
130 | RETVAL = mop_generate_constructor_method_xs(aTHX_ self, instance_vtbl); |
131 | OUTPUT: |
132 | RETVAL |
133 | |