File Coverage

XSConstructor.xs
Criterion Covered Total %
statement 893 1176 75.9
branch 478 958 49.9
condition n/a
subroutine n/a
pod n/a
total 1371 2134 64.2


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT /* we want efficiency */
2              
3             #include "xshelper.h"
4             #include "Clone.xs"
5              
6             #define IsObject(sv) (SvROK(sv) && SvOBJECT(SvRV(sv)))
7             #define IsArrayRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVAV)
8             #define IsHashRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVHV)
9             #define IsCodeRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) == SVt_PVCV)
10             #define IsScalarRef(sv) (SvROK(sv) && !SvOBJECT(SvRV(sv)) && SvTYPE(SvRV(sv)) <= SVt_PVMG)
11              
12             #define XSCON_xc_stash(a) ( (HV*)XSCON_av_at((a), XSCON_XC_STASH) )
13              
14             struct delete_ent_ctx {
15             HV *hv;
16             SV *key;
17             };
18              
19             static void
20 4           delete_mutex(pTHX_ void *p)
21             {
22 4           struct delete_ent_ctx *ctx = (struct delete_ent_ctx *)p;
23 4           hv_delete_ent(ctx->hv, ctx->key, G_DISCARD, 0);
24 4           }
25              
26             static void
27 4           dec_sv_refcnt(pTHX_ void *p)
28             {
29 4           SV *sv = (SV *)p;
30 4           SvREFCNT_dec(sv);
31 4           }
32              
33             enum {
34             XSCON_FLAG_REQUIRED = 1,
35             XSCON_FLAG_HAS_TYPE_CONSTRAINT = 2,
36             XSCON_FLAG_HAS_TYPE_COERCION = 4,
37             XSCON_FLAG_HAS_DEFAULT = 8,
38             XSCON_FLAG_NO_INIT_ARG = 16,
39             XSCON_FLAG_HAS_INIT_ARG = 32,
40             XSCON_FLAG_HAS_TRIGGER = 64,
41             XSCON_FLAG_WEAKEN = 128,
42             XSCON_FLAG_HAS_ALIASES = 256,
43             XSCON_FLAG_HAS_SLOT_INITIALIZER = 512,
44             XSCON_FLAG_UNDEF_TOLERANT = 1024,
45             XSCON_FLAG_CLONE_ON_WRITE = 2048,
46              
47             XSCON_BITSHIFT_DEFAULTS = 16,
48             XSCON_BITSHIFT_TYPES = 24,
49             };
50              
51             enum {
52             XSCON_TYPE_BASE_ANY = 0,
53             XSCON_TYPE_BASE_DEFINED = 1,
54             XSCON_TYPE_BASE_REF = 2,
55             XSCON_TYPE_BASE_BOOL = 3,
56             XSCON_TYPE_BASE_INT = 4,
57             XSCON_TYPE_BASE_PZINT = 5,
58             XSCON_TYPE_BASE_NUM = 6,
59             XSCON_TYPE_BASE_PZNUM = 7,
60             XSCON_TYPE_BASE_STR = 8,
61             XSCON_TYPE_BASE_NESTR = 9,
62             XSCON_TYPE_BASE_CLASSNAME = 10,
63             XSCON_TYPE_BASE_OBJECT = 12,
64             XSCON_TYPE_BASE_SCALARREF = 13,
65             XSCON_TYPE_BASE_CODEREF = 14,
66              
67             XSCON_TYPE_OTHER = 15,
68              
69             XSCON_TYPE_ARRAYREF = 16,
70             XSCON_TYPE_HASHREF = 32,
71             };
72              
73             enum {
74             XSCON_DEFAULT_UNDEF = 1,
75             XSCON_DEFAULT_ZERO = 2,
76             XSCON_DEFAULT_ONE = 3,
77             XSCON_DEFAULT_FALSE = 4,
78             XSCON_DEFAULT_TRUE = 5,
79             XSCON_DEFAULT_EMPTY_STR = 6,
80             XSCON_DEFAULT_EMPTY_ARRAY = 7,
81             XSCON_DEFAULT_EMPTY_HASH = 8,
82             };
83              
84             typedef struct {
85             char *name;
86             I32 flags;
87             char *init_arg;
88              
89             char **aliases;
90             I32 num_aliases;
91              
92             SV *default_sv;
93             SV *trigger_sv;
94             CV *check_cv;
95             CV *coercion_cv;
96             CV *slot_initializer_cv;
97             CV *cloner_cv;
98             } xscon_param_t;
99              
100             typedef struct {
101             char *package;
102             bool is_placeholder;
103              
104             CV *buildargs_cv;
105             CV *foreignbuildargs_cv;
106             CV *foreignconstructor_cv;
107             bool foreignbuildall;
108              
109             xscon_param_t *params;
110             I32 num_params;
111              
112             CV **build_methods;
113             I32 num_build_methods;
114              
115             bool strict_params;
116             char **allow;
117             I32 num_allow;
118             } xscon_constructor_t;
119              
120             typedef struct {
121             char *package;
122             bool is_placeholder;
123              
124             CV **demolish_methods;
125             I32 num_demolish_methods;
126             } xscon_destructor_t;
127              
128             typedef struct {
129             char *slot;
130             bool has_default;
131             I32 default_flags;
132             SV *default_sv;
133             bool has_check;
134             I32 check_flags;
135             CV *check_cv;
136             bool has_coercion;
137             CV *coercion_cv;
138             bool should_clone;
139             CV *cloner_cv;
140             } xscon_reader_t;
141              
142             typedef struct {
143             char *slot;
144             char *method_name;
145             bool has_curried;
146             AV *curried;
147             bool is_accessor;
148             bool is_try;
149             } xscon_delegation_t;
150              
151             xscon_constructor_t*
152 33           xscon_constructor_get_metadata(SV *sig_sv, xscon_constructor_t* sig) {
153              
154             dTHX;
155 33           dSP;
156              
157 33 50         if (!sig_sv) {
158 33 50         if ( !sig ) {
159 0           croak("Expected sig_sv or sig");
160             }
161 33           ENTER;
162 33           SAVETMPS;
163 33 50         PUSHMARK(SP);
164 33 50         XPUSHs(sv_2mortal(newSVpv(sig->package, 0)));
165 33           PUTBACK;
166 33           I32 count = call_pv("Class::XSConstructor::get_metadata", G_SCALAR);
167 33 50         if ( count < 1 ) {
168 0           croak("get_metadata did not return anything");
169             }
170 33           SPAGAIN;
171 33           SV *sv = POPs;
172 33           sig_sv = newSVsv(sv);
173 33           PUTBACK;
174 33 50         FREETMPS;
175 33           LEAVE;
176             }
177              
178             /* Validate and dereference the top-level hashref */
179 33 50         if (!SvROK(sig_sv) || SvTYPE(SvRV(sig_sv)) != SVt_PVHV) {
    50          
180 0           croak("signature must be a hashref");
181             }
182 33           HV *sig_hv = (HV *)SvRV(sig_sv);
183              
184             SV **svp;
185             I32 i, j;
186              
187             /* Allocate the signature struct */
188 33 50         if ( sig == NULL ) {
189 0           Newxz(sig, 1, xscon_constructor_t);
190             }
191             else {
192 33 100         if (sig->params) {
193 2 100         for (i = 0; i < sig->num_params; i++) {
194 1           xscon_param_t *p = &sig->params[i];
195 1           Safefree(p->name);
196 1           Safefree(p->init_arg);
197 1 50         for (j = 0; j < p->num_aliases; j++)
198 0           Safefree(p->aliases[j]);
199 1           Safefree(p->aliases);
200 1           SvREFCNT_dec(p->default_sv);
201 1           SvREFCNT_dec(p->trigger_sv);
202 1           SvREFCNT_dec(p->check_cv);
203 1           SvREFCNT_dec(p->coercion_cv);
204 1           SvREFCNT_dec(p->cloner_cv);
205 1           SvREFCNT_dec(p->slot_initializer_cv);
206             }
207 1           Safefree(sig->params);
208             }
209 33 100         if (sig->allow) {
210 3 100         for (j = 0; j < sig->num_allow; j++)
211 2           Safefree(sig->allow[j]);
212 1           Safefree(sig->allow);
213             }
214 33 50         if (sig->build_methods) {
215 0 0         for (i = 0; i < sig->num_build_methods; i++) {
216 0 0         if (sig->build_methods[i]) {
217 0           SvREFCNT_dec(sig->build_methods[i]);
218             }
219             }
220 0           Safefree(sig->build_methods);
221             }
222 33 50         if (sig->foreignbuildargs_cv)
223 0           Safefree(sig->foreignbuildargs_cv);
224 33 50         if (sig->foreignconstructor_cv)
225 0           Safefree(sig->foreignconstructor_cv);
226 33 50         if (sig->buildargs_cv)
227 0           Safefree(sig->buildargs_cv);
228             }
229              
230             /* This is not a placeholder. */
231 33           sig->is_placeholder = FALSE;
232              
233             /* Extract package */
234 33           svp = hv_fetchs(sig_hv, "package", 0);
235 33 50         if (svp && *svp && SvOK(*svp)) {
    50          
    50          
236 33           sig->package = savepv(SvPV_nolen(*svp));
237             } else {
238 0           sig->package = NULL;
239             }
240              
241             /* buildargs_cv */
242 33           svp = hv_fetchs(sig_hv, "buildargs", 0);
243 33 50         if (svp && SvOK(*svp)) {
    100          
244 3           sig->buildargs_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
245             } else {
246 30           sig->buildargs_cv = NULL;
247             }
248              
249             /* foreignbuildargs_cv */
250 33           svp = hv_fetchs(sig_hv, "foreignbuildargs", 0);
251 33 100         if (svp && SvOK(*svp)) {
    100          
252 1           sig->foreignbuildargs_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
253             } else {
254 32           sig->foreignbuildargs_cv = NULL;
255             }
256              
257             /* foreignconstructor_cv */
258 33           svp = hv_fetchs(sig_hv, "foreignconstructor", 0);
259 33 100         if (svp && SvOK(*svp)) {
    50          
260 3           sig->foreignconstructor_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
261             } else {
262 30           sig->foreignconstructor_cv = NULL;
263             }
264              
265             /* foreignbuildall */
266 33           svp = hv_fetchs(sig_hv, "foreignbuildall", 0);
267 33 100         if (svp && SvOK(*svp)) {
    100          
268 1           sig->foreignbuildall = TRUE;
269             } else {
270 32           sig->foreignbuildall = FALSE;
271             }
272              
273             /* Get build methods */
274             {
275             I32 count;
276 33           ENTER;
277 33           SAVETMPS;
278 33 50         PUSHMARK(SP);
279 33 50         XPUSHs(sv_2mortal(newSVpv(sig->package, 0)));
280 33           PUTBACK;
281 33           count = call_pv("Class::XSConstructor::get_build_methods", G_ARRAY);
282 33           SPAGAIN;
283 33 100         if (count > 0) {
284 6           Newxz(sig->build_methods, count, CV *);
285 6           sig->num_build_methods = count;
286 15 100         for (i = count - 1; i >= 0; i--) {
287 9           SV *sv = POPs;
288 9 50         if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVCV) {
    50          
289 0           croak("get_build_methods must return only coderefs");
290             }
291 9           sig->build_methods[i] = (CV *)SvREFCNT_inc(SvRV(sv));
292             }
293             }
294             else {
295 27           sig->num_build_methods = 0;
296             }
297 33           PUTBACK;
298 33 50         FREETMPS;
299 33           LEAVE;
300             }
301              
302             /* Extract strict_params */
303 33           svp = hv_fetchs(sig_hv, "strict_params", 0);
304 33 100         sig->strict_params = (svp && *svp && SvTRUE(*svp));
    50          
    50          
305              
306             /* Extract aliases (arrayref of strings) */
307 33           svp = hv_fetchs(sig_hv, "allow", 0);
308 33 100         if (sig->strict_params && svp && *svp) {
    50          
    50          
309 10 50         if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVAV) {
    50          
310 0           croak("allow must be an arrayref");
311             }
312              
313 10           AV *aav = (AV *)SvRV(*svp);
314 10           I32 na = av_len(aav) + 1;
315 10           sig->num_allow = na;
316 10           Newxz(sig->allow, na, char *);
317 43 100         for (j = 0; j < na; j++) {
318 33           SV **asv = av_fetch(aav, j, 0);
319 33 50         if (!asv || !*asv || !SvOK(*asv)) {
    50          
    50          
320 0           croak("allow value must be a string");
321             }
322 33           sig->allow[j] = savepv(SvPV_nolen(*asv));
323             }
324             }
325              
326             /* Fetch and validate params arrayref */
327 33           svp = hv_fetchs(sig_hv, "params", 0);
328 33 50         if (!svp || !*svp || !SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVAV) {
    50          
    50          
    50          
329 0           croak("'params' must be an arrayref");
330             }
331 33           AV *params_av = (AV *)SvRV(*svp);
332              
333             /* Allocate the params array */
334 33           I32 num_params = av_len(params_av) + 1;
335 33           sig->num_params = num_params;
336 33           Newxz(sig->params, num_params, xscon_param_t);
337              
338             /* Iterate over params array */
339 101 100         for (i = 0; i < num_params; i++) {
340              
341             /* Extract param hashref */
342 68           SV **elem = av_fetch(params_av, i, 0);
343 68 50         if (!elem || !*elem || !SvROK(*elem) || SvTYPE(SvRV(*elem)) != SVt_PVHV) {
    50          
    50          
    50          
344 0           croak("params[%d] must be a hashref", i);
345             }
346              
347 68           HV *phv = (HV *)SvRV(*elem);
348 68           xscon_param_t *p = &sig->params[i];
349              
350             /* Extract simple scalar fields */
351              
352             /* name */
353 68           svp = hv_fetchs(phv, "name", 0);
354 68 50         if (!svp || !*svp || !SvOK(*svp)) {
    50          
    50          
355 0           croak("params[%d]{name} is required", i);
356             }
357 68           p->name = savepv(SvPV_nolen(*svp));
358              
359             /* flags */
360 68           svp = hv_fetchs(phv, "flags", 0);
361 68 50         p->flags = (svp && *svp) ? SvIV(*svp) : 0;
    50          
362              
363             /* init_arg */
364 68           svp = hv_fetchs(phv, "init_arg", 0);
365 68 50         if (svp && *svp && SvOK(*svp)) {
    50          
    100          
366 65           p->init_arg = savepv(SvPV_nolen(*svp));
367             }
368             else {
369 3           p->init_arg = NULL;
370             }
371              
372             /* Extract aliases (arrayref of strings) */
373 68           svp = hv_fetchs(phv, "aliases", 0);
374 68 100         if (svp && *svp) {
    50          
375 1 50         if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVAV) {
    50          
376 0           croak("aliases must be an arrayref");
377             }
378              
379 1           AV *aav = (AV *)SvRV(*svp);
380 1           I32 na = av_len(aav) + 1;
381 1           p->num_aliases = na;
382 1           Newxz(p->aliases, na, char *);
383 2 100         for (j = 0; j < na; j++) {
384 1           SV **asv = av_fetch(aav, j, 0);
385 1 50         if (!asv || !*asv || !SvOK(*asv)) {
    50          
    50          
386 0           croak("alias must be a string");
387             }
388 1           p->aliases[j] = savepv(SvPV_nolen(*asv));
389             }
390             }
391              
392 68           svp = hv_fetchs(phv, "default", 0);
393 68 100         if (svp && SvOK(*svp)) {
    50          
394 8           p->default_sv = SvREFCNT_inc(*svp);
395             } else {
396 60           p->default_sv = NULL;
397             }
398              
399 68           svp = hv_fetchs(phv, "trigger", 0);
400 68 100         if (svp && SvOK(*svp)) {
    50          
401 3           p->trigger_sv = SvREFCNT_inc(*svp);
402             } else {
403 65           p->trigger_sv = NULL;
404             }
405              
406 68           svp = hv_fetchs(phv, "check", 0);
407 68 100         if (svp && SvOK(*svp)) {
    50          
408 7 50         if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVCV)
    50          
409 0           croak("check must be a coderef");
410 7           p->check_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
411             }
412             else {
413 61           p->check_cv = NULL;
414             }
415              
416 68           svp = hv_fetchs(phv, "coercion", 0);
417 68 100         if (svp && SvOK(*svp)) {
    50          
418 1 50         if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVCV)
    50          
419 0           croak("coercion must be a coderef");
420 1           p->coercion_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
421             }
422             else {
423 67           p->coercion_cv = NULL;
424             }
425              
426 68           svp = hv_fetchs(phv, "slot_initializer", 0);
427 68 100         if (svp && SvOK(*svp)) {
    50          
428 1 50         if (!SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVCV)
    50          
429 0           croak("slot_initializer must be a coderef");
430 1           p->slot_initializer_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
431             }
432             else {
433 67           p->slot_initializer_cv = NULL;
434             }
435              
436 68           svp = hv_fetchs(phv, "clone_on_write", 0);
437 68 100         if (svp && SvOK(*svp)) {
    50          
438 2 100         if (SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVCV) {
    50          
439 1           p->cloner_cv = (CV *)SvREFCNT_inc(SvRV(*svp));
440             }
441             else {
442 1           p->cloner_cv = NULL;
443             }
444             }
445             else {
446 66           p->cloner_cv = NULL;
447             }
448             }
449            
450 33           return sig;
451             }
452              
453             xscon_destructor_t*
454 1           xscon_destructor_get_metadata(char *packagename, xscon_destructor_t* sig) {
455              
456             dTHX;
457 1           dSP;
458              
459             I32 i;
460              
461             /* Allocate the signature struct */
462 1 50         if ( sig == NULL ) {
463 0           Newxz(sig, 1, xscon_destructor_t);
464             }
465             else {
466 1 50         if (sig->demolish_methods) {
467 0 0         for (i = 0; i < sig->num_demolish_methods; i++) {
468 0 0         if (sig->demolish_methods[i]) {
469 0           SvREFCNT_dec(sig->demolish_methods[i]);
470             }
471             }
472 0           Safefree(sig->demolish_methods);
473             }
474             }
475              
476             /* This is not a placeholder. */
477 1           sig->is_placeholder = FALSE;
478              
479             /* Extract package */
480 1           sig->package = packagename;
481              
482             /* Get demolish methods */
483             {
484 1           ENTER;
485 1           SAVETMPS;
486 1 50         PUSHMARK(SP);
487 1 50         XPUSHs(sv_2mortal(newSVpv(sig->package, 0)));
488 1           PUTBACK;
489 1           I32 count = call_pv("Class::XSConstructor::get_demolish_methods", G_ARRAY);
490 1           SPAGAIN;
491 1 50         if (count > 0) {
492 1           Newxz(sig->demolish_methods, count, CV *);
493 1           sig->num_demolish_methods = count;
494 2 100         for (i = count - 1; i >= 0; i--) {
495 1           SV *sv = POPs;
496 1 50         if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVCV) {
    50          
497 0           croak("get_demolish_methods must return only coderefs");
498             }
499 1           sig->demolish_methods[i] = (CV *)SvREFCNT_inc(SvRV(sv));
500             }
501             }
502             else {
503 0           sig->num_demolish_methods = 0;
504             }
505 1           PUTBACK;
506 1 50         FREETMPS;
507 1           LEAVE;
508             }
509              
510 1           return sig;
511             }
512              
513             SV*
514 6           join_with_commas(AV *av) {
515             dTHX;
516              
517 6           SV *out = newSVpvs("");
518 6           I32 len = av_len(av) + 1;
519             I32 i;
520              
521 19 100         for (i = 0; i <= len; i++) {
522 13           SV **svp = av_fetch(av, i, 0);
523 13 100         if (!svp) continue;
524 7 100         if (i > 0)
525 1           sv_catpvs(out, ", ");
526 7           sv_catsv(out, *svp);
527             }
528              
529 6           return out;
530             }
531              
532             static HV*
533 80           xscon_buildargs(const xscon_constructor_t* sig, const char* klass, I32 ax, I32 items) {
534             dTHX;
535             HV* args;
536              
537 80 100         if ( sig->buildargs_cv ) {
538 3           dSP;
539 3           ENTER;
540 3           SAVETMPS;
541 3 50         PUSHMARK(SP);
542             I32 i;
543 13 100         for (i = 0; i < items; i++) {
544 10 50         XPUSHs( newSVsv(ST(i)) );
545             }
546 3           PUTBACK;
547 3           int count = call_sv((SV *)sig->buildargs_cv, G_SCALAR);
548 3 50         if ( count < 1 ) {
549 0           croak("BUILDARGS did not return anything");
550             }
551 3           SPAGAIN;
552 3           SV* got = POPs;
553 3           SV* args_ref = newSVsv(got);
554 3 50         FREETMPS;
555 3           LEAVE;
556 3 50         if (!IsHashRef(args_ref)) {
    50          
    50          
557 0           croak("BUILDARGS did not return a hashref");
558             }
559 3           return (HV*)SvRV(args_ref);
560             }
561              
562             /* shift @_ */
563 77           ax++;
564 77           items--;
565              
566 77 100         if(items == 1){
567 14           SV* const args_ref = ST(0);
568 14 50         if(!IsHashRef(args_ref)){
    50          
    50          
569 0           croak("Single parameters to new() must be a HASH ref");
570             }
571 14           args = newHVhv((HV*)SvRV(args_ref));
572 14           sv_2mortal((SV*)args);
573             }
574             else{
575             I32 i;
576              
577 63 50         if( (items % 2) != 0 ){
578 0           croak("Odd number of parameters to new()");
579             }
580              
581 63           args = newHV_mortal();
582 183 100         for(i = 0; i < items; i += 2){
583 120           (void)hv_store_ent(args, ST(i), newSVsv(ST(i+1)), 0U);
584             }
585              
586             }
587 77           return args;
588             }
589              
590             static AV*
591 2           xscon_foreignbuildargs(const xscon_constructor_t* sig, const char* klass, AV* args, I32 context) {
592              
593             dTHX;
594 2           dSP;
595              
596             /* Case 1: no foreignbuildargs_cv → return @_, the class name shifted off */
597 2 100         if (!sig->foreignbuildargs_cv) {
598 1           SvREFCNT_dec(av_shift(args));
599 1           return args;
600             }
601              
602             /* Case 2: call foreignbuildargs CV */
603 1           ENTER;
604 1           SAVETMPS;
605              
606             I32 i;
607              
608 1 50         PUSHMARK(SP);
609 1           I32 argslen = av_len(args);
610 5 100         for (i = 0; i <= argslen; i++) {
611 4           SV **svp = av_fetch(args, i, 0);
612 4 50         XPUSHs(svp ? *svp : &PL_sv_undef);
    50          
613             }
614 1           PUTBACK;
615              
616 1           I32 count = call_sv((SV *)sig->foreignbuildargs_cv, context);
617              
618 1           SPAGAIN;
619              
620 1           AV* av = newAV();
621             /* copy return values into AV */
622 1           av_extend(av, count);
623 3 100         for (i = 0; i < count; i++) {
624 2           SV *sv = POPs;
625 2           av_store(av, count - ( i + 1 ), newSVsv(sv));
626             }
627              
628 1           PUTBACK;
629 1 50         FREETMPS;
630 1           LEAVE;
631              
632 1           return av;
633             }
634              
635             static SV*
636 3           xscon_foreignconstructor(const xscon_constructor_t* sig, const char* klass, AV* fbargs) {
637             dTHX;
638 3           dSP;
639              
640             SV *ret;
641              
642             /* Must have a constructor CV */
643 3 50         if (!sig->foreignconstructor_cv) {
644 0           croak("No foreign constructor defined for class %s", klass);
645             }
646              
647             /* --- call constructor in scalar context --- */
648 3           ENTER;
649 3           SAVETMPS;
650              
651             I32 i;
652              
653 3 50         PUSHMARK(SP);
654              
655             /* push class name as first argument */
656 3 50         XPUSHs(sv_2mortal(newSVpv(klass, 0)));
657              
658             /* push fbargs as list */
659 3           I32 n = av_len(fbargs);
660 8 100         for (i = 0; i <= n; i++) {
661 5           SV **svp = av_fetch(fbargs, i, 0);
662 5 50         XPUSHs(svp ? *svp : &PL_sv_undef);
    50          
663             }
664              
665 3           PUTBACK;
666              
667 3           I32 count = call_sv((SV *)sig->foreignconstructor_cv, G_SCALAR);
668              
669 3           SPAGAIN;
670            
671 3 50         if (count != 1) {
672 0 0         FREETMPS;
673 0           LEAVE;
674 0           croak("Foreign constructor did not return a value");
675             }
676              
677 3           ret = POPs;
678              
679             /* take ownership before temporaries are freed */
680 3           SvREFCNT_inc(ret);
681            
682 3           PUTBACK;
683 3 50         FREETMPS;
684 3           LEAVE;
685              
686             /* --- validate return value --- */
687              
688 3 50         if (SvROK(ret) && sv_isobject(ret)) {
    50          
689            
690             /* same class? */
691 3 100         if (sv_isa(ret, klass)) {
692 1           return ret;
693             }
694              
695             /* different class → re-bless */
696 2           HV *newstash = gv_stashpv(klass, GV_ADD);
697 2 50         if (!newstash) {
698 0           SvREFCNT_dec(ret);
699 0           croak("Cannot find stash for class %s", klass);
700             }
701              
702 2           sv_bless(ret, newstash);
703 2           return ret;
704             }
705            
706 0           SvREFCNT_dec(ret);
707 0           croak("Foreign constructor did not return an object");
708             }
709              
710             static SV*
711 77           xscon_create_instance(const xscon_constructor_t* sig, const char* klass) {
712             dTHX;
713             SV* instance;
714 77           instance = sv_bless( newRV_noinc((SV*)newHV()), gv_stashpv(klass, 1) );
715 77           return sv_2mortal(instance);
716             }
717              
718             static bool
719 5           _S_pv_is_integer (char* const pv) {
720             dTHX;
721             const char* p;
722 5           p = &pv[0];
723              
724             /* -?[0-9]+ */
725 5 50         if(*p == '-') p++;
726              
727 5 50         if (!*p) return FALSE;
728              
729 9 100         while(*p){
730 7 100         if(!isDIGIT(*p)){
731 3           return FALSE;
732             }
733 4           p++;
734             }
735 2           return TRUE;
736             }
737              
738             static bool
739 0           _S_nv_is_integer (NV const nv) {
740             dTHX;
741 0 0         if(nv == (NV)(IV)nv){
742 0           return TRUE;
743             }
744             else {
745             char buf[64]; /* Must fit sprintf/Gconvert of longest NV */
746 0           intptr_t ignored = (intptr_t)Gconvert(nv, NV_DIG, 0, buf);
747             (void)ignored;
748 0           return _S_pv_is_integer(buf);
749             }
750             }
751              
752             bool
753 0           _is_class_loaded (SV* const klass ) {
754             dTHX;
755             HV *stash;
756             GV** gvp;
757             HE* he;
758              
759 0 0         if ( !SvPOKp(klass) || !SvCUR(klass) ) { /* XXX: SvPOK does not work with magical scalars */
    0          
760 0           return FALSE;
761             }
762              
763 0           stash = gv_stashsv( klass, FALSE );
764 0 0         if ( !stash ) {
765 0           return FALSE;
766             }
767              
768 0 0         if (( gvp = (GV**)hv_fetchs(stash, "VERSION", FALSE) )) {
769 0 0         if ( isGV(*gvp) && GvSV(*gvp) && SvOK(GvSV(*gvp)) ){
    0          
    0          
770 0           return TRUE;
771             }
772             }
773              
774 0 0         if (( gvp = (GV**)hv_fetchs(stash, "ISA", FALSE) )) {
775 0 0         if ( isGV(*gvp) && GvAV(*gvp) && av_len(GvAV(*gvp)) != -1 ) {
    0          
    0          
776 0           return TRUE;
777             }
778             }
779              
780 0           hv_iterinit(stash);
781 0 0         while (( he = hv_iternext(stash) )) {
782 0           GV* const gv = (GV*)HeVAL(he);
783 0 0         if ( isGV(gv) ) {
784 0 0         if ( GvCVu(gv) ) { /* is GV and has CV */
    0          
785 0           hv_iterinit(stash); /* reset */
786 0           return TRUE;
787             }
788             }
789 0 0         else if ( SvOK(gv) ) { /* is a stub or constant */
790 0           hv_iterinit(stash); /* reset */
791 0           return TRUE;
792             }
793             }
794 0           return FALSE;
795             }
796              
797             static bool
798 21           xscon_check_type(char* keyname, SV* const val, int flags, CV* check_cv)
799             {
800             dTHX;
801             assert(val);
802              
803             /* An unknown type constraint
804             * We need to use check_cv.
805             */
806 21 100         if ( ( flags & XSCON_TYPE_OTHER ) == XSCON_TYPE_OTHER ) {
807 15 50         if ( !check_cv ) {
808 0 0         warn( "Type constraint check coderef gone AWOL for attribute '%s', so just assuming value passes", keyname ? keyname : "unknown" );
809 0           return 1;
810             }
811            
812             SV* result;
813              
814 15           dSP;
815 15           ENTER;
816 15           SAVETMPS;
817 15 50         PUSHMARK(SP);
818 15 50         EXTEND(SP, 1);
819 15           PUSHs(sv_2mortal(val));
820 15           PUTBACK;
821 15           int count = call_sv((SV *)check_cv, G_SCALAR | G_EVAL);
822 15           SPAGAIN;
823 15 50         result = count ? POPs : &PL_sv_undef;
824 15           bool return_val = SvTRUE(result);
825 15 50         FREETMPS;
826 15           LEAVE;
827            
828 15           return return_val;
829             }
830            
831 6 50         if ( flags & XSCON_TYPE_ARRAYREF ) {
832 0 0         if ( !IsArrayRef(val) ) {
    0          
    0          
833 0           return FALSE;
834             }
835 0 0         if ( flags == XSCON_TYPE_ARRAYREF ) {
836 0           return TRUE;
837             }
838 0           int newflags = flags & ( XSCON_TYPE_ARRAYREF - 1 );
839 0           AV* const av = (AV*)SvRV(val);
840 0           I32 const len = av_len(av) + 1;
841             I32 i;
842 0 0         for (i = 0; i < len; i++) {
843 0           SV* const subval = *av_fetch(av, i, TRUE);
844 0 0         if ( ! xscon_check_type(NULL, subval, newflags, NULL) ) {
845 0           return FALSE;
846             }
847             }
848 0           return TRUE;
849             }
850              
851 6 50         if ( flags & XSCON_TYPE_HASHREF ) {
852 0 0         if ( !IsHashRef(val) ) {
    0          
    0          
853 0           return FALSE;
854             }
855             /* HashRef[Any] or HashRef */
856 0 0         if ( flags == XSCON_TYPE_HASHREF ) {
857 0           return TRUE;
858             }
859 0           int newflags = flags & ( XSCON_TYPE_HASHREF - 1 );
860 0           HV* const hv = (HV*)SvRV(val);
861             HE* he;
862 0           hv_iterinit(hv);
863 0 0         while ((he = hv_iternext(hv))) {
864 0           SV* const subval = hv_iterval(hv, he);
865 0 0         if ( ! xscon_check_type(NULL, subval, newflags, NULL) ) {
866 0           hv_iterinit(hv); /* reset */
867 0           return FALSE;
868             }
869             }
870 0           return TRUE;
871             }
872            
873 6           switch ( flags ) {
874 0           case XSCON_TYPE_BASE_ANY:
875 0           return TRUE;
876 0           case XSCON_TYPE_BASE_DEFINED:
877 0           return SvOK(val);
878 0           case XSCON_TYPE_BASE_REF:
879 0 0         return SvOK(val) && SvROK(val);
    0          
880 0           case XSCON_TYPE_BASE_BOOL:
881 0 0         if ( SvROK(val) || isGV(val) ) {
    0          
882 0           return FALSE;
883             }
884 0 0         else if ( sv_true( val ) ) {
885 0 0         if ( SvPOKp(val) ) {
886             /* String "1" */
887 0 0         return SvCUR(val) == 1 && SvPVX(val)[0] == '1';
    0          
888             }
889 0 0         else if ( SvIOKp(val) ) {
890             /* Integer 1 */
891 0           return SvIVX(val) == 1;
892             }
893 0 0         else if( SvNOKp(val) ) {
894             /* Float 1.0 */
895 0           return SvNVX(val) == 1.0;
896             }
897             else {
898             /* Another way to check for string "1"??? */
899             STRLEN len;
900 0           char* ptr = SvPV(val, len);
901 0 0         return len == 1 && ptr[0] == '1';
    0          
902             }
903             }
904             else {
905             /* Any non-reference non-true value (0, undef, "", "0")
906             * is a valid Bool.
907             */
908 0           return TRUE;
909             }
910 6           case XSCON_TYPE_BASE_INT:
911 6 50         if ( SvOK(val) && !SvROK(val) && !isGV(val) ) {
    50          
    50          
912 6 100         if ( SvPOK(val) ) {
913 5           return _S_pv_is_integer( SvPVX(val) );
914             }
915 1 50         else if ( SvIOK(val) ) {
916 1           return TRUE;
917             }
918 0 0         else if ( SvNOK(val) ) {
919 0           return _S_nv_is_integer( SvNVX(val) );
920             }
921             }
922 0           return FALSE;
923 0           case XSCON_TYPE_BASE_PZINT:
924             /* Discard non-integers */
925 0 0         if ( (!SvOK(val)) || SvROK(val) || isGV(val) ) {
    0          
    0          
926 0           return FALSE;
927             }
928 0 0         if ( SvPOKp(val) ){
929 0 0         if ( ! _S_pv_is_integer( SvPVX(val) ) ) {
930 0           return FALSE;
931             }
932             }
933 0 0         else if ( SvIOKp(val) ) {
934             /* ok */
935             }
936 0 0         else if ( SvNOKp(val) ) {
937 0 0         if ( ! _S_nv_is_integer( SvNVX(val) ) ) {
938 0           return FALSE;
939             }
940             }
941              
942             /* Check that the string representation is non-empty and
943             * doesn't start with a minus sign. We already checked
944             * for strings that don't look like integers at all.
945             */
946             STRLEN len;
947 0           char* i = SvPVx(val, len);
948 0 0         return ( (len > 0 && i[0] != '-') ? TRUE : FALSE );
    0          
949 0           case XSCON_TYPE_BASE_NUM:
950             /* In Perl We Trust */
951 0           return looks_like_number(val);
952 0           case XSCON_TYPE_BASE_PZNUM:
953 0 0         if ( ! looks_like_number(val) ) {
954 0           return FALSE;
955             }
956 0           NV numeric = SvNV(val);
957 0           return numeric >= 0.0;
958 0           case XSCON_TYPE_BASE_STR:
959 0 0         return SvOK(val) && !SvROK(val) && !isGV(val);
    0          
    0          
960 0           case XSCON_TYPE_BASE_NESTR:
961 0 0         if ( SvOK(val) && !SvROK(val) && !isGV(val) ) {
    0          
    0          
962 0           STRLEN l = sv_len(val);
963 0           return ( (l==0) ? FALSE : TRUE );
964             }
965 0           return FALSE;
966 0           case XSCON_TYPE_BASE_CLASSNAME:
967 0           return _is_class_loaded(val);
968 0           case 11:
969             /* might use later */
970 0           croak("PANIC!");
971 0           case XSCON_TYPE_BASE_OBJECT:
972 0 0         return IsObject(val);
    0          
973 0           case XSCON_TYPE_BASE_SCALARREF:
974 0 0         return IsScalarRef(val);
    0          
    0          
975 0           case XSCON_TYPE_BASE_CODEREF:
976 0 0         return IsCodeRef(val);
    0          
    0          
977 0           case XSCON_TYPE_OTHER:
978             /* Should have already been checked by if block at start of function. */
979 0           croak("PANIC!");
980 0           default:
981             /* Should never happen */
982 0           croak("PANIC!");
983             } /* switch ( flags ) */
984             }
985              
986             SV*
987 11           xscon_run_default(SV *object, char* keyname, int has_common_default, SV *default_sv)
988             {
989             dTHX;
990              
991 11           switch ( has_common_default ) {
992 0           case XSCON_DEFAULT_UNDEF:
993 0           return newSV(0);
994 0           case XSCON_DEFAULT_ZERO:
995 0           return newSViv(0);
996 2           case XSCON_DEFAULT_ONE:
997 2           return newSViv(1);
998 0           case XSCON_DEFAULT_FALSE:
999 0           return &PL_sv_no;
1000 0           case XSCON_DEFAULT_TRUE:
1001 0           return &PL_sv_yes;
1002 0           case XSCON_DEFAULT_EMPTY_STR:
1003 0           return newSVpvs("");
1004 1           case XSCON_DEFAULT_EMPTY_ARRAY:
1005 1           AV *av = newAV();
1006 1           return newRV_noinc((SV*)av);
1007 1           case XSCON_DEFAULT_EMPTY_HASH:
1008 1           HV *hv = newHV();
1009 1           return newRV_noinc((SV*)hv);
1010             }
1011              
1012 7 50         if ( !default_sv ) {
1013 0           croak("Attribute '%s' is required, but default is AWOL", keyname);
1014             return &PL_sv_no;
1015             }
1016              
1017             /* Coderef, call as method */
1018 7 100         if (IsCodeRef( default_sv )) {
    50          
    100          
1019 3           dSP;
1020 3           ENTER;
1021 3           SAVETMPS;
1022 3 50         PUSHMARK(SP);
1023 3 50         EXTEND(SP, 1);
1024 3           PUSHs(object);
1025 3           PUTBACK;
1026 3           int count = call_sv((SV*)default_sv, G_SCALAR);
1027 3           SPAGAIN;
1028 3 50         SV* got = count ? POPs : &PL_sv_undef;
1029 3           SV* val = newSVsv(got);
1030 3 50         FREETMPS;
1031 3           LEAVE;
1032 3           return val;
1033             }
1034              
1035             /* Scalarref to the name of a builder, call as method */
1036 4 100         if (IsScalarRef(default_sv)) {
    50          
    50          
1037             STRLEN len;
1038 1           SV *method_name_sv = SvRV(default_sv);
1039 1           char *method_name = SvPV(method_name_sv, len);
1040 1           dSP;
1041 1           ENTER;
1042 1           SAVETMPS;
1043 1 50         PUSHMARK(SP);
1044 1 50         EXTEND(SP, 1);
1045 1           PUSHs(object);
1046 1           PUTBACK;
1047 1           int count = call_method(method_name, G_SCALAR);
1048 1           SPAGAIN;
1049 1 50         SV* got = count ? POPs : &PL_sv_undef;
1050 1           SV* val = newSVsv(got);
1051 1 50         FREETMPS;
1052 1           LEAVE;
1053 1           return val;
1054             }
1055              
1056 3           return newSVsv(default_sv);
1057             }
1058              
1059             void
1060 4           xscon_run_trigger(SV *object,
1061             xscon_param_t* param)
1062             {
1063             dTHX;
1064 4           dSP;
1065              
1066 4           char* attr_name = param->name;
1067 4           STRLEN attr_len = strlen(attr_name);
1068              
1069             SV *mutexkey_sv;
1070             SV **svp;
1071             SV *value_sv;
1072              
1073 4           HV *object_hv = (HV *)SvRV(object);
1074              
1075 4           mutexkey_sv = newSV(attr_len + sizeof(":trigger_mutex") - 1);
1076 4           sv_setpvn(mutexkey_sv, attr_name, attr_len);
1077 4           sv_catpvs(mutexkey_sv, ":trigger_mutex");
1078              
1079 4 50         if (hv_exists_ent(object_hv, mutexkey_sv, 0)) {
1080 0           SvREFCNT_dec(mutexkey_sv);
1081 0           return;
1082             }
1083              
1084 4           hv_store_ent(object_hv, mutexkey_sv, newSViv(1), 0);
1085              
1086 4           ENTER;
1087 4           SAVETMPS;
1088              
1089             /* Ensure the key SV is released */
1090 4           SAVEDESTRUCTOR_X(dec_sv_refcnt, (void *)mutexkey_sv);
1091              
1092             /* Ensure the mutex is deleted on scope exit */
1093             struct delete_ent_ctx *ctx;
1094 4           Newxz(ctx, 1, struct delete_ent_ctx);
1095 4           ctx->hv = object_hv;
1096 4           ctx->key = mutexkey_sv;
1097 4           SAVEDESTRUCTOR_X(delete_mutex, ctx);
1098              
1099 4           svp = hv_fetch(object_hv, attr_name, attr_len, 0);
1100 4 50         value_sv = svp ? *svp : &PL_sv_undef;
1101              
1102 4           SV* trigger_sv = param->trigger_sv;
1103              
1104 4 100         if (!SvROK(trigger_sv)) {
1105 3 50         PUSHMARK(SP);
1106 3 50         XPUSHs((SV *)object);
1107 3 50         XPUSHs(value_sv);
1108 3           PUTBACK;
1109 3           call_method(SvPV_nolen(trigger_sv), G_VOID);
1110             }
1111 1 50         else if (SvTYPE(SvRV(trigger_sv)) == SVt_PVCV) {
1112 1 50         PUSHMARK(SP);
1113 1 50         XPUSHs((SV *)object);
1114 1 50         XPUSHs(value_sv);
1115 1           PUTBACK;
1116 1           call_sv(trigger_sv, G_VOID);
1117             }
1118             else {
1119 0           croak("Unexpected trigger type");
1120             }
1121              
1122 4 50         FREETMPS;
1123 4           LEAVE;
1124             }
1125              
1126             int
1127 80           xscon_initialize_object(const xscon_constructor_t* sig, const char* klass, SV* const object, HV* const args, bool const is_cloning)
1128             {
1129             dTHX;
1130              
1131             assert(sig);
1132             assert(object);
1133             assert(args);
1134              
1135 80 50         if (sig->is_placeholder) {
1136 0           croak("Called on a placeholder");
1137             }
1138              
1139 80 50         if(mg_find((SV*)args, PERL_MAGIC_tied)){
1140 0           croak("You cannot use tied HASH reference as initializing arguments");
1141             }
1142              
1143             I32 i;
1144 80           int used = 0;
1145              
1146             /* we can weaken everything at the end */
1147 80           AV *weakrefs = NULL;
1148              
1149             /* copy allowed attributes */
1150 284 100         for (i = 0; i < sig->num_params; i++) {
1151 225           xscon_param_t *param = &sig->params[i];
1152 225           int flags = param->flags;
1153 225           char *keyname = param->name;
1154 225           int keylen = strlen(param->name);
1155 225           char *init_arg = param->init_arg;
1156 225           int init_arg_len = -1;
1157 225 100         if ( param->init_arg ) {
1158 222           init_arg_len = strlen(param->init_arg);
1159             }
1160              
1161             SV** valref;
1162             SV* val;
1163 225           bool has_value = FALSE;
1164 225           bool value_was_from_args = FALSE;
1165              
1166 225 100         if ( (!( flags & XSCON_FLAG_NO_INIT_ARG )) && init_arg_len >= 0 && hv_exists(args, init_arg, init_arg_len) ) {
    50          
    100          
1167             /* Value provided in args hash */
1168 111           valref = hv_fetch(args, init_arg, init_arg_len, 0);
1169 111           val = newSVsv(*valref);
1170 111           has_value = TRUE;
1171 111           value_was_from_args = TRUE;
1172 111           used++;
1173             }
1174              
1175 225 100         if ( flags & XSCON_FLAG_HAS_ALIASES ) {
1176             I32 i;
1177 5 100         for (i = 0; i < param->num_aliases; i++) {
1178 3           char *alias = param->aliases[i];
1179 3           int alias_len = strlen(alias);
1180 3 100         if ( hv_exists(args, alias, alias_len) ) {
1181 2 100         if ( has_value ) {
1182 1           croak("Superfluous alias used for attribute '%s': %s", keyname, alias);
1183             }
1184             else {
1185 1           valref = hv_fetch(args, alias, alias_len, 0);
1186 1           val = newSVsv(*valref);
1187 1           has_value = TRUE;
1188 1           value_was_from_args = TRUE;
1189 1           used++;
1190             }
1191             }
1192             }
1193             }
1194              
1195 224 100         if ( value_was_from_args && ( flags & XSCON_FLAG_UNDEF_TOLERANT ) && !SvOK(val) ) {
    100          
    100          
1196 1           has_value = FALSE;
1197 1           val = NULL;
1198             }
1199              
1200 224 100         if ( !has_value && flags & XSCON_FLAG_HAS_DEFAULT ) {
    100          
1201             /* There is a default/builder
1202             * Some very common defaults are worth hardcoding into the flags
1203             * so we won't need to do anything expensive to fill them in.
1204             */
1205 9           I32 has_common_default = ( flags >> XSCON_BITSHIFT_DEFAULTS ) & 255;
1206 9           val = xscon_run_default( object, keyname, has_common_default, param->default_sv );
1207 9           has_value = TRUE;
1208 9           value_was_from_args = FALSE;
1209             }
1210              
1211             /* Type checks and coercions */
1212 224 100         if ( has_value && ( flags & XSCON_FLAG_HAS_TYPE_CONSTRAINT ) ) {
    100          
1213 15           int type_flags = flags >> XSCON_BITSHIFT_TYPES;
1214 15           bool failed = !xscon_check_type(keyname, newSVsv(val), type_flags, param->check_cv);
1215            
1216             /* we failed type check */
1217 15 100         if ( failed ) {
1218 9 100         if ( flags & XSCON_FLAG_HAS_TYPE_COERCION && param->coercion_cv ) {
    50          
1219             SV* newval;
1220 3           dSP;
1221 3           ENTER;
1222 3           SAVETMPS;
1223 3 50         PUSHMARK(SP);
1224 3 50         EXTEND(SP, 1);
1225 3           PUSHs(val);
1226 3           PUTBACK;
1227 3           int count = call_sv((SV *)param->coercion_cv, G_SCALAR);
1228 3           SPAGAIN;
1229 3 50         SV* tmpval = count ? POPs : &PL_sv_undef;
1230 3           newval = newSVsv(tmpval);
1231 3 50         FREETMPS;
1232 3           LEAVE;
1233            
1234 3           bool passed_this_time = xscon_check_type(keyname, newSVsv(newval), type_flags, param->check_cv);
1235 3 100         if ( passed_this_time ) {
1236 2           val = newSVsv(newval);
1237             }
1238             else {
1239 1           croak("Coercion result '%s' failed type constraint for '%s'", SvPV_nolen(newval), keyname);
1240             }
1241             }
1242             else {
1243 4           croak("Value '%s' failed type constraint for '%s'", SvPV_nolen(val), keyname);
1244             }
1245             }
1246             }
1247            
1248 219 100         if ( has_value ) {
1249 114 100         if ( value_was_from_args && ( flags & XSCON_FLAG_CLONE_ON_WRITE ) ) {
    100          
1250 2 100         if ( param->cloner_cv ) {
1251             SV* newval;
1252 1           dSP;
1253 1           ENTER;
1254 1           SAVETMPS;
1255 1 50         PUSHMARK(SP);
1256 1 50         EXTEND(SP, 3);
1257 1           PUSHs(object);
1258 1           PUSHs(newSVpv(keyname, keylen));
1259 1           PUSHs(val);
1260 1           PUTBACK;
1261 1           int count = call_sv((SV *)param->cloner_cv, G_SCALAR);
1262 1           SPAGAIN;
1263 1 50         SV* tmpval = count ? POPs : val;
1264 1           newval = newSVsv(tmpval);
1265 1 50         FREETMPS;
1266 1           LEAVE;
1267 1           bool passed_this_time = xscon_check_type(keyname, newSVsv(newval), flags >> XSCON_BITSHIFT_TYPES, param->check_cv);
1268 1 50         if ( passed_this_time ) {
1269 0           val = newSVsv(newval);
1270             }
1271             else {
1272 1           croak("Cloning result '%s' failed type constraint for '%s'", SvPV_nolen(newval), keyname);
1273             }
1274             }
1275             else {
1276 1           HV *hseen = newHV();
1277 1 50         if ( weakrefs == NULL ) weakrefs = newAV();
1278 1           SV *newval = sv_clone(aTHX_ val, hseen, -1, 0, weakrefs);
1279 1           hv_clear(hseen);
1280 1           SvREFCNT_dec((SV *)hseen);
1281 1           val = newval;
1282             }
1283             }
1284              
1285 114 100         if ( ( flags & XSCON_FLAG_HAS_SLOT_INITIALIZER ) && param->slot_initializer_cv ) {
    50          
1286 1           dSP;
1287 1           ENTER;
1288 1           SAVETMPS;
1289 1 50         PUSHMARK(SP);
1290 1 50         EXTEND(SP, 2);
1291 1           PUSHs(object);
1292 1           PUSHs(val);
1293 1           PUTBACK;
1294 1           (void)call_sv((SV *)param->slot_initializer_cv, G_VOID);
1295 1           SPAGAIN;
1296 1 50         FREETMPS;
1297 1           LEAVE;
1298             }
1299             else {
1300 112           (void)hv_store((HV*)SvRV(object), keyname, keylen, val, 0);
1301             }
1302              
1303 113 100         if ( value_was_from_args && ( flags & XSCON_FLAG_HAS_TRIGGER ) ) {
    100          
1304 4           xscon_run_trigger(object, param);
1305             }
1306            
1307 113 100         if ( SvROK(val) && flags & XSCON_FLAG_WEAKEN ) {
    100          
1308 1 50         if ( weakrefs == NULL ) weakrefs = newAV();
1309 1           PUSH_WEAKREFS( weakrefs, val );
1310             }
1311             }
1312 105 100         else if ( flags & XSCON_FLAG_REQUIRED ) {
1313 14 50         if ( flags & XSCON_FLAG_HAS_INIT_ARG && strcmp(keyname, init_arg) != 0 ) {
    0          
1314 0           croak("Attribute '%s' (init arg '%s') is required", keyname, init_arg);
1315             }
1316             else {
1317 14           croak("Attribute '%s' is required", keyname);
1318             }
1319             }
1320             }
1321              
1322 60 100         if ( weakrefs ) HANDLE_WEAKREFS( weakrefs );
    50          
    50          
    50          
    100          
1323              
1324 59           return used;
1325             }
1326              
1327             static void
1328 60           xscon_buildall(const xscon_constructor_t* sig, const char* klass, SV* const object, SV* const args) {
1329             dTHX;
1330 60           dSP;
1331              
1332             assert(object);
1333             assert(args);
1334              
1335 60           HV* args_hv = (HV *)SvRV(args);
1336             I32 i;
1337              
1338             /* __no_BUILD__ support */
1339 60 100         if (hv_exists(args_hv, "__no_BUILD__", 12)) {
1340 2           SV *val = hv_delete(args_hv, "__no_BUILD__", 12, 0);
1341 2 50         if ( SvOK(val) && SvTRUE(val) ) {
    100          
1342 1           return;
1343             }
1344             }
1345              
1346             /* If we can take the fast route... */
1347 59 100         if ( strcmp(klass, sig->package) == 0 ) {
1348 57 100         if ( sig->num_build_methods <= 0 )
1349 48           return;
1350 23 100         for (i = 0; i < sig->num_build_methods; i++) {
1351 14           CV *cv = sig->build_methods[i];
1352 14 50         if (!cv)
1353 0           continue;
1354 14           ENTER;
1355 14           SAVETMPS;
1356 14 50         PUSHMARK(SP);
1357 14 50         EXTEND(SP, 2);
1358 14           PUSHs(object);
1359 14           PUSHs(args);
1360 14           PUTBACK;
1361 14           call_sv((SV *)cv, G_VOID);
1362 14 100         FREETMPS;
1363 14           LEAVE;
1364             }
1365 9           return;
1366             }
1367              
1368             /* Fall back to slow route because BUILDALL called on a subclass */
1369 2           HV* const stash = gv_stashpv("Class::XSConstructor", 1);
1370             assert(stash != NULL);
1371            
1372 2           SV *pkgsv = newSVpv(sig->package, 0);
1373 2           SV *klasssv = newSVpv(klass, 0);
1374            
1375             /* get cache stuff */
1376 2           SV** const globref = hv_fetch(stash, "BUILD_CACHE", 11, 0);
1377 2           HV* buildall_hash = buildall_hash = GvHV(*globref);
1378 2           STRLEN klass_len = strlen(klass);
1379 2           SV** buildall = hv_fetch(buildall_hash, klass, klass_len, 0);
1380            
1381 2 50         if ( !buildall || !SvOK(*buildall) ) {
    0          
1382 2           ENTER;
1383 2           SAVETMPS;
1384 2 50         PUSHMARK(SP);
1385 2 50         EXTEND(SP, 2);
1386 2           PUSHs(pkgsv);
1387 2           PUSHs(klasssv);
1388 2           PUTBACK;
1389 2           (void)call_pv("Class::XSConstructor::populate_build", G_VOID);
1390 2           PUTBACK;
1391 2 50         FREETMPS;
1392 2           LEAVE;
1393 2           buildall = hv_fetch(buildall_hash, klass, klass_len, 0);
1394             }
1395            
1396 2 50         if (!buildall || !SvOK(*buildall)) {
    50          
1397 0           croak("something should have happened!");
1398             }
1399            
1400 2 50         if (!SvROK(*buildall)) {
1401 0           return;
1402             }
1403              
1404 2           AV* const builds = (AV*)SvRV(*buildall);
1405 2           I32 const len = av_len(builds) + 1;
1406             SV** tmp;
1407             SV* build;
1408              
1409 6 100         for (i = 0; i < len; i++) {
1410 4           tmp = av_fetch(builds, i, 0);
1411             assert(tmp);
1412 4           build = *tmp;
1413            
1414 4           dSP;
1415 4           ENTER;
1416 4           SAVETMPS;
1417 4 50         PUSHMARK(SP);
1418 4 50         EXTEND(SP, 2);
1419 4           PUSHs(object);
1420 4           PUSHs(args);
1421 4           PUTBACK;
1422 4           (void)call_sv(build, G_VOID);
1423 4           PUTBACK;
1424 4 50         FREETMPS;
1425 4           LEAVE;
1426             }
1427             }
1428              
1429             static void
1430 2           xscon_demolishall(const xscon_destructor_t* sig, const char* klass, SV* object, bool use_eval, AV* args) {
1431             dTHX;
1432 2           dSP;
1433              
1434             assert(object);
1435              
1436             I32 i, j;
1437              
1438             /* If we can take the fast route... */
1439 2 50         if ( strcmp(klass, sig->package) == 0 ) {
1440 0 0         if ( sig->num_demolish_methods <= 0 )
1441 0           return;
1442 0 0         for (i = 0; i < sig->num_demolish_methods; i++) {
1443 0           CV *cv = sig->demolish_methods[i];
1444 0 0         if (!cv)
1445 0           continue;
1446 0           ENTER;
1447 0           SAVETMPS;
1448 0 0         PUSHMARK(SP);
1449 0 0         EXTEND(SP, 2);
1450 0           PUSHs(object);
1451 0           I32 n = av_len(args);
1452 0 0         for (j = 0; j <= n; j++) {
1453 0           SV **svp = av_fetch(args, j, 0);
1454 0 0         XPUSHs(svp ? *svp : &PL_sv_undef);
    0          
1455             }
1456 0           PUTBACK;
1457 0 0         (void)call_sv((SV *)cv, use_eval ? ( G_VOID | G_EVAL ) : G_VOID);
1458 0 0         FREETMPS;
1459 0           LEAVE;
1460             }
1461 0           return;
1462             }
1463              
1464             /* Fall back to slow route because DEMOLISHALL called on a subclass */
1465 2           HV* const stash = gv_stashpv("Class::XSConstructor", 1);
1466             assert(stash != NULL);
1467            
1468 2           SV *pkgsv = newSVpv(sig->package, 0);
1469 2           SV *klasssv = newSVpv(klass, 0);
1470            
1471             /* get cache stuff */
1472 2           SV** const globref = hv_fetch(stash, "DEMOLISH_CACHE", 14, 0);
1473 2           HV* demolishall_hash = GvHV(*globref);
1474            
1475 2           STRLEN klass_len = strlen(klass);
1476 2           SV** demolishall = hv_fetch(demolishall_hash, klass, klass_len, 0);
1477            
1478 2 100         if ( !demolishall || !SvOK(*demolishall) ) {
    50          
1479 1           ENTER;
1480 1           SAVETMPS;
1481 1 50         PUSHMARK(SP);
1482 1 50         EXTEND(SP, 2);
1483 1           PUSHs(pkgsv);
1484 1           PUSHs(klasssv);
1485 1           PUTBACK;
1486 1           (void)call_pv("Class::XSConstructor::populate_demolish", G_VOID);
1487 1           PUTBACK;
1488 1 50         FREETMPS;
1489 1           LEAVE;
1490            
1491 1           demolishall = hv_fetch(demolishall_hash, klass, klass_len, 0);
1492             }
1493            
1494 2 50         if (!SvOK(*demolishall)) {
1495 0           croak("something should have happened!");
1496             }
1497            
1498 2 50         if (!SvROK(*demolishall)) {
1499 0           return;
1500             }
1501              
1502 2           AV* const demolishes = (AV*)SvRV(*demolishall);
1503 2           I32 const len = av_len(demolishes) + 1;
1504             SV** tmp;
1505             SV* demolish;
1506              
1507 6 100         for (i = 0; i < len; i++) {
1508 4           tmp = av_fetch(demolishes, i, 0);
1509             assert(tmp);
1510 4           demolish = *tmp;
1511            
1512 4           dSP;
1513 4           ENTER;
1514 4           SAVETMPS;
1515 4 50         PUSHMARK(SP);
1516 4 50         EXTEND(SP, 2);
1517 4           PUSHs(object);
1518 4           I32 n = av_len(args);
1519 10 100         for (j = 0; j <= n; j++) {
1520 6           SV **svp = av_fetch(args, j, 0);
1521 6 50         XPUSHs(svp ? *svp : &PL_sv_undef);
    50          
1522             }
1523 4           PUTBACK;
1524 4 50         (void)call_sv(demolish, use_eval ? ( G_VOID | G_EVAL ) : G_VOID);
1525 4           PUTBACK;
1526 4 50         FREETMPS;
1527 4           LEAVE;
1528             }
1529             }
1530              
1531             static void
1532 7           xscon_strictcon(const xscon_constructor_t* sig, const char* klassname, SV* const object, SV* const args) {
1533             dTHX;
1534              
1535             assert(object);
1536             assert(args);
1537              
1538 7 50         if ( ! sig->strict_params ) {
1539 0           return;
1540             }
1541              
1542 7           AV *badattrs = newAV();
1543              
1544 7           HV* argshv = (HV*)SvRV(args);
1545             HE* he;
1546             I32 i;
1547              
1548 7           hv_iterinit(argshv);
1549 19 100         while ((he = hv_iternext(argshv))) {
1550 12           SV* const k = hv_iterkeysv(he);
1551             STRLEN k_len;
1552 12           char *k_str = SvPV(k, k_len);
1553 12           bool found = FALSE;
1554              
1555 44 100         for (i = 0; i < sig->num_allow; i++) {
1556 37 100         if (k_len == strlen(sig->allow[i]) && memcmp(k_str, sig->allow[i], k_len) == 0) {
    100          
1557 5           found = TRUE;
1558 5           break;
1559             }
1560             }
1561              
1562 12 100         if (!found) {
1563 7           av_push(badattrs, k);
1564             }
1565             }
1566              
1567 7           I32 const badattrs_len = av_len(badattrs) + 1;
1568 7 100         if ( badattrs_len > 0 ) {
1569 6           SV* const badattrs_commas = join_with_commas(badattrs);
1570 6 100         if ( badattrs_len == 1 ) {
1571 5           croak("Found unknown attribute passed to the constructor: %s", SvPV_nolen(badattrs_commas));
1572             }
1573             else {
1574 1           croak("Found unknown attributes passed to the constructor: %s", SvPV_nolen(badattrs_commas));
1575             }
1576             }
1577             }
1578              
1579             MODULE = Class::XSConstructor PACKAGE = Class::XSConstructor
1580              
1581             BOOT:
1582             {
1583 25           HV *stash = gv_stashpv("Class::XSConstructor", GV_ADD);
1584              
1585 25           newCONSTSUB(stash, "XSCON_FLAG_REQUIRED", newSViv(XSCON_FLAG_REQUIRED));
1586 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_TYPE_CONSTRAINT", newSViv(XSCON_FLAG_HAS_TYPE_CONSTRAINT));
1587 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_TYPE_COERCION", newSViv(XSCON_FLAG_HAS_TYPE_COERCION));
1588 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_DEFAULT", newSViv(XSCON_FLAG_HAS_DEFAULT));
1589 25           newCONSTSUB(stash, "XSCON_FLAG_NO_INIT_ARG", newSViv(XSCON_FLAG_NO_INIT_ARG));
1590 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_INIT_ARG", newSViv(XSCON_FLAG_HAS_INIT_ARG));
1591 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_TRIGGER", newSViv(XSCON_FLAG_HAS_TRIGGER));
1592 25           newCONSTSUB(stash, "XSCON_FLAG_WEAKEN", newSViv(XSCON_FLAG_WEAKEN));
1593 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_ALIASES", newSViv(XSCON_FLAG_HAS_ALIASES));
1594 25           newCONSTSUB(stash, "XSCON_FLAG_HAS_SLOT_INITIALIZER", newSViv(XSCON_FLAG_HAS_SLOT_INITIALIZER));
1595 25           newCONSTSUB(stash, "XSCON_FLAG_UNDEF_TOLERANT", newSViv(XSCON_FLAG_UNDEF_TOLERANT));
1596 25           newCONSTSUB(stash, "XSCON_FLAG_CLONE_ON_WRITE", newSViv(XSCON_FLAG_CLONE_ON_WRITE));
1597              
1598 25           newCONSTSUB(stash, "XSCON_BITSHIFT_DEFAULTS", newSViv(XSCON_BITSHIFT_DEFAULTS));
1599 25           newCONSTSUB(stash, "XSCON_BITSHIFT_TYPES", newSViv(XSCON_BITSHIFT_TYPES));
1600              
1601 25           newCONSTSUB(stash, "XSCON_DEFAULT_UNDEF", newSViv(XSCON_DEFAULT_UNDEF));
1602 25           newCONSTSUB(stash, "XSCON_DEFAULT_ZERO", newSViv(XSCON_DEFAULT_ZERO));
1603 25           newCONSTSUB(stash, "XSCON_DEFAULT_ONE", newSViv(XSCON_DEFAULT_ONE));
1604 25           newCONSTSUB(stash, "XSCON_DEFAULT_FALSE", newSViv(XSCON_DEFAULT_FALSE));
1605 25           newCONSTSUB(stash, "XSCON_DEFAULT_TRUE", newSViv(XSCON_DEFAULT_TRUE));
1606 25           newCONSTSUB(stash, "XSCON_DEFAULT_EMPTY_STR", newSViv(XSCON_DEFAULT_EMPTY_STR));
1607 25           newCONSTSUB(stash, "XSCON_DEFAULT_EMPTY_ARRAY", newSViv(XSCON_DEFAULT_EMPTY_ARRAY));
1608 25           newCONSTSUB(stash, "XSCON_DEFAULT_EMPTY_HASH", newSViv(XSCON_DEFAULT_EMPTY_HASH));
1609              
1610 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_ANY", newSViv(XSCON_TYPE_BASE_ANY));
1611 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_DEFINED", newSViv(XSCON_TYPE_BASE_DEFINED));
1612 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_REF", newSViv(XSCON_TYPE_BASE_REF));
1613 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_BOOL", newSViv(XSCON_TYPE_BASE_BOOL));
1614 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_INT", newSViv(XSCON_TYPE_BASE_INT));
1615 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_PZINT", newSViv(XSCON_TYPE_BASE_PZINT));
1616 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_NUM", newSViv(XSCON_TYPE_BASE_NUM));
1617 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_PZNUM", newSViv(XSCON_TYPE_BASE_PZNUM));
1618 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_STR", newSViv(XSCON_TYPE_BASE_STR));
1619 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_NESTR", newSViv(XSCON_TYPE_BASE_NESTR));
1620 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_CLASSNAME", newSViv(XSCON_TYPE_BASE_CLASSNAME));
1621 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_OBJECT", newSViv(XSCON_TYPE_BASE_OBJECT));
1622 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_SCALARREF", newSViv(XSCON_TYPE_BASE_SCALARREF));
1623 25           newCONSTSUB(stash, "XSCON_TYPE_BASE_CODEREF", newSViv(XSCON_TYPE_BASE_CODEREF));
1624              
1625 25           newCONSTSUB(stash, "XSCON_TYPE_OTHER", newSViv(XSCON_TYPE_OTHER));
1626              
1627 25           newCONSTSUB(stash, "XSCON_TYPE_ARRAYREF", newSViv(XSCON_TYPE_ARRAYREF));
1628 25           newCONSTSUB(stash, "XSCON_TYPE_HASHREF", newSViv(XSCON_TYPE_HASHREF));
1629             }
1630              
1631             void
1632             new_object(SV* klass, ...)
1633             CODE:
1634             {
1635             dTHX;
1636             /* dSP; */
1637              
1638             const char* klassname;
1639             SV* args;
1640             SV* object;
1641             I32 i;
1642              
1643 80           xscon_constructor_t *sig = (xscon_constructor_t *) CvXSUBANY(cv).any_ptr;
1644 80 100         if (sig->is_placeholder) xscon_constructor_get_metadata(NULL, sig);
1645              
1646             /* $klassname = shift */
1647 80 50         klassname = SvROK(klass) ? sv_reftype(SvRV(klass), 1) : SvPV_nolen_const(klass);
1648              
1649 80           bool need_to_remove_no_build = FALSE;
1650              
1651 80 100         if ( sig->foreignbuildall ) {
1652 1 50         if ( sig->foreignbuildargs_cv ) {
1653             /* @fbargs = scalar $foreign->BUILDARGS( @_ ) */
1654 0           AV *av = newAV();
1655 0 0         for (i = 0; i < items; i++) {
1656 0           av_push(av, newSVsv(ST(i)));
1657             }
1658 0           AV* fbargs = xscon_foreignbuildargs(sig, klassname, av, G_SCALAR);
1659              
1660             /* $args = $fbargs[0] */
1661 0           SV** svp = av_fetch(fbargs, 0, 0);
1662 0           args = newSVsv(*svp);
1663              
1664 0 0         if ( !args || !IsHashRef(args)) {
    0          
    0          
    0          
1665 0           croak("Parent BUILDARGS did not return a hashref");
1666             }
1667             }
1668             else {
1669             /* $args = ref($_[0]) eq 'HASH' ? %{+shift} : @_ */
1670 1           args = newRV_inc((SV*)xscon_buildargs(sig, klassname, ax, items));
1671 1           sv_2mortal(args);
1672             }
1673              
1674 1 50         if ( !hv_exists((HV*)SvRV(args), "__no_BUILD__", 12) ) {
1675             /* $args{__no_BUILD__} = !!1 */
1676 1           (void)hv_store((HV*)SvRV(args), "__no_BUILD__", 12, &PL_sv_yes, 0);
1677 1           need_to_remove_no_build = TRUE;
1678             }
1679              
1680             /* @args_but_list = ( $args ) */
1681 1           AV *args_but_list = newAV();
1682 1           av_push( args_but_list, newSVsv(args) );
1683              
1684             /* $object = $klassname->SUPER::new( @args_but_list ); */
1685 1           object = xscon_foreignconstructor(sig, klassname, args_but_list);
1686              
1687 1 50         if ( need_to_remove_no_build ) {
1688 1           (void)hv_delete((HV*)SvRV(args), "__no_BUILD__", 12, G_DISCARD);
1689 1           need_to_remove_no_build = FALSE;
1690             }
1691             }
1692 79 100         else if ( sig->foreignconstructor_cv ) {
1693             /* @fbargs = $klassname->can('FOREIGNBUILDARGS') ? $klassname->FOREIGNBUILDARGS( @_ ) : @_ */
1694 2           AV *av = newAV();
1695 9 100         for (i = 0; i < items; i++) {
1696 7           av_push(av, newSVsv(ST(i)));
1697             }
1698 2           AV* fbargs = xscon_foreignbuildargs(sig, klassname, av, G_ARRAY);
1699              
1700             /* $object = $klassname->SUPER::new( @fbargs ); */
1701 2           object = xscon_foreignconstructor(sig, klassname, fbargs);
1702              
1703             /* $args = ref($_[0]) eq 'HASH' ? %{+shift} : @_ */
1704 2           args = newRV_inc((SV*)xscon_buildargs(sig, klassname, ax, items));
1705 2           sv_2mortal(args);
1706             }
1707             else {
1708             /* $args = ref($_[0]) eq 'HASH' ? %{+shift} : @_ */
1709 77           args = newRV_inc((SV*)xscon_buildargs(sig, klassname, ax, items));
1710 77           sv_2mortal(args);
1711              
1712             /* $object = bless( {}, $klassname ); */
1713 77           object = xscon_create_instance(sig, klassname);
1714             }
1715              
1716             /* Initialize parameters: returns the number of keys in args that were actually used */
1717 80           int used = xscon_initialize_object(sig, klassname, object, (HV*)SvRV(args), FALSE);
1718              
1719             /* Call BUILD methods */
1720 59           xscon_buildall(sig, klassname, object, args);
1721              
1722             /* Strict constructor */
1723 59 100         if ( sig->strict_params ) {
1724 17 50         if ( used < HvUSEDKEYS((HV*)SvRV(args)) ) {
    100          
1725 7           xscon_strictcon(sig, klassname, object, args);
1726             }
1727             }
1728              
1729             /* return $object */
1730 53           ST(0) = object; /* because object is mortal, we should return it as is */
1731 53           XSRETURN(1);
1732             }
1733              
1734             void
1735             BUILDALL(SV* object, SV* args)
1736             CODE:
1737             {
1738             dTHX;
1739              
1740 1           xscon_constructor_t *sig = (xscon_constructor_t *) CvXSUBANY(cv).any_ptr;
1741 1 50         if (sig->is_placeholder) xscon_constructor_get_metadata(NULL, sig);
1742              
1743 1           const char *klassname = NULL;
1744 1           HV *stash = SvSTASH(SvRV(object));
1745 1 50         if (!stash) croak("Not a blessed object?");
1746 1 50         klassname = HvNAME(stash);
    50          
    50          
    0          
    50          
    50          
1747              
1748             /* Call BUILD methods */
1749 1           xscon_buildall(sig, klassname, object, args);
1750              
1751             /* return $object */
1752 1           ST(0) = object; /* because object is mortal, we should return it as is */
1753 1           XSRETURN(1);
1754             }
1755              
1756             void
1757             XSCON_CLEAR_CONSTRUCTOR_CACHE(SV* proto)
1758             CODE:
1759             {
1760             dTHX;
1761              
1762 1           xscon_constructor_t *sig = (xscon_constructor_t *) CvXSUBANY(cv).any_ptr;
1763 1           sig->is_placeholder = TRUE;
1764              
1765             /* return $proto */
1766 1           ST(0) = proto;
1767 1           XSRETURN(1);
1768             }
1769              
1770             void
1771             destroy(SV* object, ...)
1772             CODE:
1773             {
1774             dTHX;
1775 1           xscon_destructor_t *sig = (xscon_destructor_t *) CvXSUBANY(cv).any_ptr;
1776 1 50         if (sig->is_placeholder) xscon_destructor_get_metadata(sig->package, sig);
1777            
1778 1 50         const char* klassname = SvROK(object) ? sv_reftype(SvRV(object), 1) : SvPV_nolen_const(object);
1779 1           AV* args = newAV();
1780 1           av_push( args, newSViv(PL_dirty) );
1781 1           xscon_demolishall(sig, klassname, object, FALSE, args);
1782 1           XSRETURN(0);
1783             }
1784              
1785             void
1786             DEMOLISHALL(SV* object, ...)
1787             CODE:
1788             {
1789             dTHX;
1790 1           xscon_destructor_t *sig = (xscon_destructor_t *) CvXSUBANY(cv).any_ptr;
1791 1 50         if (sig->is_placeholder) xscon_destructor_get_metadata(sig->package, sig);
1792            
1793 1 50         const char* klassname = SvROK(object) ? sv_reftype(SvRV(object), 1) : SvPV_nolen_const(object);
1794 1           AV* args = newAV();
1795             I32 i;
1796 3 100         for (i = 1; i < items; i++) {
1797 2           av_push(args, newSVsv(ST(i)));
1798             }
1799 1           xscon_demolishall(sig, klassname, object, FALSE, args);
1800 1           XSRETURN(0);
1801             }
1802              
1803             void
1804             XSCON_CLEAR_DESTRUCTOR_CACHE(SV* proto)
1805             CODE:
1806             {
1807             dTHX;
1808              
1809 0           xscon_destructor_t *sig = (xscon_destructor_t *) CvXSUBANY(cv).any_ptr;
1810 0           sig->is_placeholder = TRUE;
1811              
1812             /* return $proto */
1813 0           ST(0) = proto;
1814 0           XSRETURN(1);
1815             }
1816              
1817             void
1818             reader(SV* object, ...)
1819             CODE:
1820             {
1821             dTHX;
1822 5           dSP;
1823            
1824 5           xscon_reader_t *sig = (xscon_reader_t *) CvXSUBANY(cv).any_ptr;
1825            
1826 5           HV *object_hv = (HV *)SvRV(object);
1827 5           STRLEN slotlen = strlen(sig->slot);
1828            
1829 5 100         if ( sig->has_default && !hv_exists(object_hv, sig->slot, slotlen) ) {
    50          
1830 2           SV* val = xscon_run_default(object, sig->slot, sig->default_flags, sig->default_sv);
1831 2 50         if ( sig->has_check && !xscon_check_type(sig->slot, newSVsv(val), sig->check_flags, sig->check_cv) ) {
    100          
1832 1 50         if ( sig->has_coercion ) {
1833             SV* newval;
1834 0           ENTER;
1835 0           SAVETMPS;
1836 0 0         PUSHMARK(SP);
1837 0 0         EXTEND(SP, 1);
1838 0           PUSHs(val);
1839 0           PUTBACK;
1840 0           int count = call_sv((SV *)sig->coercion_cv, G_SCALAR);
1841 0           SPAGAIN;
1842 0 0         SV* tmpval = count ? POPs : &PL_sv_undef;
1843 0           newval = newSVsv(tmpval);
1844 0 0         FREETMPS;
1845 0           LEAVE;
1846            
1847 0 0         if ( xscon_check_type(sig->slot, newSVsv(newval), sig->check_flags, sig->check_cv) ) {
1848 0           val = newSVsv(newval);
1849             }
1850             else {
1851 0           croak("Coercion result '%s' failed type constraint for '%s'", SvPV_nolen(newval), sig->slot);
1852             }
1853             }
1854             else {
1855 1           croak("Value '%s' failed type constraint for '%s'", SvPV_nolen(val), sig->slot);
1856             }
1857             }
1858 1           (void)hv_store(object_hv, sig->slot, slotlen, val, 0);
1859             }
1860            
1861 4           SV** svp = hv_fetch(object_hv, sig->slot, slotlen, 0);
1862 4 50         SV* val = svp ? newSVsv(*svp) : &PL_sv_undef;
1863              
1864 4 100         if ( sig->should_clone && sig->cloner_cv == NULL ) {
    50          
1865 3           HV *hseen = newHV();
1866 3           AV *weakrefs = newAV();
1867 3           SV *newval = sv_clone(aTHX_ val, hseen, -1, 0, weakrefs);
1868 3 0         HANDLE_WEAKREFS( weakrefs );
    0          
    0          
    50          
1869 3           hv_clear(hseen);
1870 3           SvREFCNT_dec((SV *)hseen);
1871 3           ST(0) = newval;
1872             }
1873 1 50         else if ( sig->should_clone ) {
1874             SV* newval;
1875 0           dSP;
1876 0           ENTER;
1877 0           SAVETMPS;
1878 0 0         PUSHMARK(SP);
1879 0 0         EXTEND(SP, 3);
1880 0           PUSHs(object);
1881 0           PUSHs(newSVpv(sig->slot, 0));
1882 0           PUSHs(val);
1883 0           PUTBACK;
1884 0           int count = call_sv((SV *)sig->cloner_cv, G_SCALAR);
1885 0           SPAGAIN;
1886 0 0         SV* tmpval = count ? POPs : val;
1887 0           newval = newSVsv(tmpval);
1888 0 0         FREETMPS;
1889 0           LEAVE;
1890            
1891 0           bool passed_this_time = TRUE;
1892 0 0         if ( sig->has_check ) {
1893 0           passed_this_time = xscon_check_type(sig->slot, newSVsv(newval), sig->check_flags, sig->check_cv);
1894             }
1895            
1896 0 0         if ( passed_this_time ) {
1897 0           ST(0) = newval;
1898             }
1899             else {
1900 0           croak("Cloning result '%s' failed type constraint for '%s'", SvPV_nolen(newval), sig->slot);
1901             }
1902             }
1903             else {
1904 1           ST(0) = val;
1905             }
1906              
1907 4           XSRETURN(1);
1908             }
1909              
1910             void
1911             delegation(SV* object, ...)
1912             CODE:
1913             {
1914             dTHX;
1915 8           dSP;
1916            
1917 8           I32 gimme = GIMME_V;
1918 8           bool inc = FALSE;
1919              
1920 8           I32 nargs = items - 1;
1921 8           AV *args = newAV();
1922             I32 i;
1923 16 100         for ( i = 0; i < nargs; i++ ) {
1924 8           SV *arg = ST( i + 1 );
1925 8           av_push( args, arg );
1926             }
1927              
1928 8           xscon_delegation_t *sig = (xscon_delegation_t *) CvXSUBANY(cv).any_ptr;
1929              
1930             /* Get handler object */
1931             SV* handler;
1932 8 50         if ( sig->is_accessor ) {
1933 0           ENTER;
1934 0           SAVETMPS;
1935            
1936 0 0         PUSHMARK(SP);
1937 0 0         XPUSHs(object);
1938 0           PUTBACK;
1939            
1940 0           I32 count = call_method(sig->slot, G_SCALAR | G_EVAL);
1941 0 0         if (SvTRUE(ERRSV)) croak(NULL);
    0          
1942            
1943 0           SPAGAIN;
1944 0 0         handler = count ? POPs : &PL_sv_undef;
1945 0 0         if ( handler != &PL_sv_undef ) {
1946 0           SvREFCNT_inc(handler);
1947 0           inc = TRUE;
1948             }
1949            
1950 0           PUTBACK;
1951 0 0         FREETMPS;
1952 0           LEAVE;
1953             }
1954             else {
1955 8           HV *object_hv = (HV *)SvRV(object);
1956 8           STRLEN slotlen = strlen(sig->slot);
1957 8           SV** svp = hv_fetch(object_hv, sig->slot, slotlen, 0);
1958 8 100         handler = svp ? *svp : &PL_sv_undef;
1959 8 100         if ( handler != &PL_sv_undef ) {
1960 6           SvREFCNT_inc(handler);
1961 6           inc = TRUE;
1962             }
1963             }
1964              
1965 8 100         if ( !IsObject(handler) ) {
    100          
1966 4 100         if ( sig->is_try ) {
1967 2           ST(0) = &PL_sv_undef;
1968 2           XSRETURN(1);
1969             return;
1970             }
1971 2 100         croak(
1972             "Expected blessed object to delegate to; got %s",
1973             ( handler == &PL_sv_undef ) ? "undef" : SvPV_nolen(handler)
1974             );
1975             }
1976              
1977 4           SP = MARK;
1978            
1979 4           ENTER;
1980 4           SAVETMPS;
1981              
1982 4 50         PUSHMARK(SP);
1983 4 50         XPUSHs(handler);
1984            
1985             /* add curried arguments */
1986 4 100         if ( sig->has_curried ) {
1987 2           I32 n = av_len(sig->curried) + 1;
1988 8 100         for (i = 0; i < n; i++) {
1989 6           SV **svp = av_fetch(sig->curried, i, 0);
1990 6 50         XPUSHs( svp ? *svp : &PL_sv_undef );
    50          
1991             }
1992             }
1993            
1994             /* forward all arguments except $object */
1995 12 100         for (i = 0; i < nargs; i++) {
1996 8           SV **svp = av_fetch(args, i, 0);
1997 8 50         XPUSHs( svp ? *svp : &PL_sv_undef );
    50          
1998             }
1999            
2000 4           PUTBACK;
2001            
2002 4           I32 count = call_method(sig->method_name, gimme);
2003 4           LEAVE;
2004            
2005 4 50         if (inc) SvREFCNT_dec(handler);
2006            
2007             /* Do not SPAGAIN or POPs: return values left on stack! */
2008 4           XSRETURN(count);
2009             }
2010              
2011             void
2012             install_constructor(char* name, char* name2, char* name3)
2013             CODE:
2014             {
2015             dTHX;
2016 37           CV *cv = newXS(name, XS_Class__XSConstructor_new_object, (char*)__FILE__);
2017 37 50         if (cv == NULL)
2018 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2019              
2020 37           CV *cv2 = newXS(name2, XS_Class__XSConstructor_BUILDALL, (char*)__FILE__);
2021 37 50         if (cv2 == NULL)
2022 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2023              
2024 37           CV *cv3 = newXS(name3, XS_Class__XSConstructor_XSCON_CLEAR_CONSTRUCTOR_CACHE, (char*)__FILE__);
2025 37 50         if (cv3 == NULL)
2026 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2027              
2028 37           char *full = savepv(name);
2029 37           const char *last = NULL;
2030             char *p;
2031 99 100         for (p = full; (p = strstr(p, "::")); p += 2) {
2032 62           last = p;
2033             }
2034             char *pkg;
2035 37 50         if (last) {
2036 37           size_t len = (size_t)(last - full);
2037 37           pkg = (char *)malloc(len + 1);
2038 37           memcpy(pkg, full, len);
2039 37           pkg[len] = '\0';
2040             } else {
2041 0           pkg = strdup("");
2042             }
2043            
2044             xscon_constructor_t *sig;
2045 37           Newxz(sig, 1, xscon_constructor_t);
2046 37           sig->package = savepv(pkg);
2047 37           sig->is_placeholder = TRUE;
2048            
2049 37           CvXSUBANY(cv).any_ptr = sig;
2050 37           CvXSUBANY(cv2).any_ptr = sig;
2051 37           CvXSUBANY(cv3).any_ptr = sig;
2052             }
2053              
2054             void
2055             install_destructor(char* name, char* name2, char* name3)
2056             CODE:
2057             {
2058             dTHX;
2059 1           CV *cv = newXS(name, XS_Class__XSConstructor_destroy, (char*)__FILE__);
2060 1 50         if (cv == NULL)
2061 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2062            
2063 1           CV *cv2 = newXS(name2, XS_Class__XSConstructor_DEMOLISHALL, (char*)__FILE__);
2064 1 50         if (cv2 == NULL)
2065 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2066            
2067 1           CV *cv3 = newXS(name3, XS_Class__XSConstructor_XSCON_CLEAR_DESTRUCTOR_CACHE, (char*)__FILE__);
2068 1 50         if (cv3 == NULL)
2069 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2070              
2071 1           char *full = savepv(name);
2072 1           const char *last = NULL;
2073             char *p;
2074 3 100         for (p = full; (p = strstr(p, "::")); p += 2) {
2075 2           last = p;
2076             }
2077             char *pkg;
2078 1 50         if (last) {
2079 1           size_t len = (size_t)(last - full);
2080 1           pkg = (char *)malloc(len + 1);
2081 1           memcpy(pkg, full, len);
2082 1           pkg[len] = '\0';
2083             } else {
2084 0           pkg = strdup("");
2085             }
2086            
2087             xscon_destructor_t *sig;
2088 1           Newxz(sig, 1, xscon_destructor_t);
2089 1           sig->package = savepv(pkg);
2090 1           sig->is_placeholder = TRUE;
2091 1           CvXSUBANY(cv).any_ptr = sig;
2092 1           CvXSUBANY(cv2).any_ptr = sig;
2093 1           CvXSUBANY(cv3).any_ptr = sig;
2094             }
2095              
2096             void
2097             install_delegation(char *name, char *slot, char *method_name, SV *curried, bool is_accessor, bool is_try)
2098             CODE:
2099             {
2100             dTHX;
2101 3           CV *cv = newXS(name, XS_Class__XSConstructor_delegation, (char*)__FILE__);
2102 3 50         if (cv == NULL)
2103 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2104            
2105             xscon_delegation_t *sig;
2106 3           Newxz(sig, 1, xscon_delegation_t);
2107 3           sig->slot = savepv(slot);
2108 3           sig->is_try = is_try;
2109 3           sig->is_accessor = is_accessor;
2110 3           sig->method_name = savepv(method_name);
2111            
2112 3 50         if (!curried || !SvROK(curried) || SvTYPE(SvRV(curried)) != SVt_PVAV) {
    100          
    50          
2113 2           sig->has_curried = FALSE;
2114 2           sig->curried = NULL;
2115             }
2116             else {
2117 1           AV *curried_av = (AV *)SvRV(curried);
2118 1           sig->has_curried = TRUE;
2119 1           sig->curried = (AV *)SvREFCNT_inc((SV *)curried_av);
2120             }
2121            
2122 3           CvXSUBANY(cv).any_ptr = sig;
2123             }
2124              
2125             void
2126             install_reader(char *name, char *slot, bool has_default, int default_flags, SV* default_sv, int check_flags, SV* check, SV* coercion, ...)
2127             CODE:
2128             {
2129             dTHX;
2130 3           CV *cv = newXS(name, XS_Class__XSConstructor_reader, (char*)__FILE__);
2131 3 50         if (cv == NULL)
2132 0           croak("ARG! Something went really wrong while installing a new XSUB!");
2133              
2134             xscon_reader_t *sig;
2135 3           Newxz(sig, 1, xscon_reader_t);
2136 3           sig->slot = savepv(slot);
2137 3           sig->has_default = has_default;
2138 3           sig->default_flags = default_flags;
2139 3           sig->default_sv = SvREFCNT_inc(default_sv);
2140 3           sig->check_flags = check_flags;
2141            
2142 3 50         if (check && IsCodeRef(check)) {
    50          
    50          
    50          
2143 3           sig->has_check = TRUE;
2144 3           sig->check_cv = (CV *)SvREFCNT_inc(SvRV(check));
2145             }
2146             else {
2147 0           sig->has_check = FALSE;
2148 0           sig->check_cv = NULL;
2149             }
2150            
2151 3 50         if (coercion && IsCodeRef(coercion)) {
    50          
    0          
    0          
2152 0           sig->has_coercion = TRUE;
2153 0           sig->coercion_cv = (CV *)SvREFCNT_inc(SvRV(coercion));
2154             }
2155             else {
2156 3           sig->has_coercion = FALSE;
2157 3           sig->coercion_cv = NULL;
2158             }
2159              
2160 3           SV *cloner = &PL_sv_undef;
2161 3 50         if (items >= 9) {
2162 3           cloner = ST(8);
2163             }
2164              
2165 3 50         if (cloner && IsCodeRef(cloner)) {
    50          
    0          
    0          
2166 0           sig->should_clone = TRUE;
2167 0           sig->cloner_cv = (CV *)SvREFCNT_inc(SvRV(cloner));
2168             }
2169 3 50         else if (cloner && SvTRUE(cloner)) {
    100          
2170 1           sig->should_clone = TRUE;
2171 1           sig->cloner_cv = NULL;
2172             }
2173             else {
2174 2           sig->has_coercion = FALSE;
2175 2           sig->coercion_cv = NULL;
2176             }
2177              
2178 3           CvXSUBANY(cv).any_ptr = sig;
2179             }
2180              
2181             void
2182             clone(self, depth=-1)
2183             SV *self
2184             int depth
2185             PREINIT:
2186 1           SV *clone = &PL_sv_undef;
2187 1           HV *hseen = newHV();
2188 1 50         AV *weakrefs = newAV();
2189             PPCODE:
2190             TRACEME(("ref = 0x%x\n", self));
2191 1           clone = sv_clone( aTHX_ self, hseen, depth, 0, weakrefs );
2192             /* Now apply deferred weakening.
2193             * All strong references in the clone graph are established,
2194             * so it is safe to weaken references without destroying referents. */
2195 1 0         HANDLE_WEAKREFS( weakrefs );
    0          
    0          
    50          
2196 1           hv_clear(hseen); /* Free HV */
2197 1           SvREFCNT_dec((SV *)hseen);
2198 1 50         EXTEND(SP,1);
2199 1           PUSHs(sv_2mortal(clone));