also don't leak the instance on error
[gitmo/Package-Stash-XS.git] / Stash.xs
CommitLineData
59017825 1#include "EXTERN.h"
2#include "perl.h"
3#include "XSUB.h"
4
a382a84b 5typedef enum {
6 VAR_NONE = 0,
7 VAR_SCALAR,
8 VAR_ARRAY,
9 VAR_HASH,
10 VAR_CODE,
11 VAR_IO,
12 VAR_GLOB, /* TODO: unimplemented */
13 VAR_FORMAT /* TODO: unimplemented */
14} vartype_t;
15
16typedef struct {
17 vartype_t type;
18 char sigil;
19 char *name;
20} varspec_t;
21
76f7c306 22const char *vartype_to_string(vartype_t type)
23{
24 switch (type) {
25 case VAR_SCALAR:
26 return "SCALAR";
27 case VAR_ARRAY:
28 return "ARRAY";
29 case VAR_HASH:
30 return "HASH";
31 case VAR_CODE:
32 return "CODE";
33 case VAR_IO:
34 return "IO";
35 default:
36 return "unknown";
37 }
38}
39
40I32 vartype_to_svtype(vartype_t type)
41{
42 switch (type) {
43 case VAR_SCALAR:
44 return SVt_PV; /* or whatever */
45 case VAR_ARRAY:
46 return SVt_PVAV;
47 case VAR_HASH:
48 return SVt_PVHV;
49 case VAR_CODE:
50 return SVt_PVCV;
51 case VAR_IO:
52 return SVt_PVIO;
53 default:
54 return SVt_NULL;
55 }
56}
57
a382a84b 58vartype_t string_to_vartype(char *vartype)
59{
60 if (strEQ(vartype, "SCALAR")) {
61 return VAR_SCALAR;
62 }
63 else if (strEQ(vartype, "ARRAY")) {
64 return VAR_ARRAY;
65 }
66 else if (strEQ(vartype, "HASH")) {
67 return VAR_HASH;
68 }
69 else if (strEQ(vartype, "CODE")) {
70 return VAR_CODE;
71 }
72 else if (strEQ(vartype, "IO")) {
73 return VAR_IO;
74 }
75 else {
76 croak("Type must be one of 'SCALAR', 'ARRAY', 'HASH', 'CODE', or 'IO'");
77 }
78}
79
80void _deconstruct_variable_name(char *variable, varspec_t *varspec)
81{
82 if (!variable || !variable[0])
83 croak("You must pass a variable name");
84
85 varspec->type = VAR_NONE;
86
87 switch (variable[0]) {
88 case '$':
89 varspec->type = VAR_SCALAR;
90 break;
91 case '@':
92 varspec->type = VAR_ARRAY;
93 break;
94 case '%':
95 varspec->type = VAR_HASH;
96 break;
97 case '&':
98 varspec->type = VAR_CODE;
99 break;
100 }
101
102 if (varspec->type != VAR_NONE) {
103 varspec->sigil = variable[0];
104 varspec->name = &variable[1];
105 }
106 else {
107 varspec->type = VAR_IO;
108 varspec->sigil = '\0';
109 varspec->name = variable;
110 }
111}
112
113void _deconstruct_variable_hash(HV *variable, varspec_t *varspec)
114{
115 SV **val;
a382a84b 116
117 val = hv_fetch(variable, "name", 4, 0);
118 if (!val)
119 croak("The 'name' key is required in variable specs");
120
121 varspec->name = savesvpv(*val);
301f570b 122 SAVEFREEPV(varspec->name);
a382a84b 123
124 val = hv_fetch(variable, "sigil", 5, 0);
125 if (!val)
126 croak("The 'sigil' key is required in variable specs");
127
128 varspec->sigil = (SvPV_nolen(*val))[0];
129
130 val = hv_fetch(variable, "type", 4, 0);
131 if (!val)
132 croak("The 'type' key is required in variable specs");
133
134 varspec->type = string_to_vartype(SvPV_nolen(*val));
135}
136
137int _valid_for_type(SV *value, vartype_t type)
138{
139 svtype sv_type = SvROK(value) ? SvTYPE(SvRV(value)) : SVt_NULL;
140
141 switch (type) {
142 case VAR_SCALAR:
143 return sv_type == SVt_NULL ||
144 sv_type == SVt_IV ||
145 sv_type == SVt_NV ||
146 sv_type == SVt_PV ||
147 sv_type == SVt_RV;
148 case VAR_ARRAY:
149 return sv_type == SVt_PVAV;
150 case VAR_HASH:
151 return sv_type == SVt_PVHV;
152 case VAR_CODE:
153 return sv_type == SVt_PVCV;
154 case VAR_IO:
76f7c306 155 return sv_type == SVt_PVIO;
a382a84b 156 default:
157 return 0;
158 }
159}
160
3fd56b4d 161HV *_get_namespace(SV *self)
162{
163 dSP;
164 SV *ret;
165
166 PUSHMARK(SP);
167 XPUSHs(self);
168 PUTBACK;
169
612dcf3b 170 call_method("namespace", G_SCALAR);
3fd56b4d 171
172 SPAGAIN;
173 ret = POPs;
174 PUTBACK;
175
176 return (HV*)SvRV(ret);
177}
178
76f7c306 179SV *_get_name(SV *self)
180{
181 dSP;
182 SV *ret;
183
184 PUSHMARK(SP);
185 XPUSHs(self);
186 PUTBACK;
187
612dcf3b 188 call_method("name", G_SCALAR);
76f7c306 189
190 SPAGAIN;
191 ret = POPs;
192 PUTBACK;
193
194 return ret;
195}
196
4871b1a0 197SV *_get_package_symbol(SV *self, varspec_t *variable, int vivify)
198{
199 HV *namespace;
200 SV **entry;
201 GV *glob;
202
203 namespace = _get_namespace(self);
204 entry = hv_fetch(namespace, variable->name, strlen(variable->name), vivify);
205 if (!entry)
206 return NULL;
207
208 glob = (GV*)(*entry);
209 if (!isGV(glob)) {
210 SV *namesv;
4871b1a0 211
212 namesv = newSVsv(_get_name(self));
213 sv_catpvs(namesv, "::");
214 sv_catpv(namesv, variable->name);
215
b55c97c2 216 /* can't use gv_init here, because it screws up @ISA in a way that I
217 * can't reproduce, but that CMOP triggers */
218 gv_fetchsv(namesv, GV_ADD, vartype_to_svtype(variable->type));
02b2a57f 219 SvREFCNT_dec(namesv);
4871b1a0 220 }
221
222 if (vivify) {
223 switch (variable->type) {
224 case VAR_SCALAR:
225 if (!GvSV(glob))
226 GvSV(glob) = newSV(0);
227 break;
228 case VAR_ARRAY:
229 if (!GvAV(glob))
230 GvAV(glob) = newAV();
231 break;
232 case VAR_HASH:
233 if (!GvHV(glob))
234 GvHV(glob) = newHV();
235 break;
236 case VAR_CODE:
237 croak("Don't know how to vivify CODE variables");
238 case VAR_IO:
239 if (!GvIO(glob))
240 GvIOp(glob) = newIO();
241 break;
242 default:
243 croak("Unknown type in vivication");
244 }
245 }
246
247 switch (variable->type) {
248 case VAR_SCALAR:
249 return GvSV(glob);
250 case VAR_ARRAY:
251 return (SV*)GvAV(glob);
252 case VAR_HASH:
253 return (SV*)GvHV(glob);
254 case VAR_CODE:
255 return (SV*)GvCV(glob);
256 case VAR_IO:
257 return (SV*)GvIO(glob);
258 default:
259 return NULL;
260 }
261}
262
59017825 263MODULE = Package::Stash PACKAGE = Package::Stash
264
be2a7e99 265PROTOTYPES: DISABLE
266
59017825 267SV*
268new(class, package_name)
269 char *class
270 SV *package_name
3496f1e8 271 PREINIT:
59017825 272 HV *instance;
273 HV *namespace;
1d6c978b 274 SV *nsref;
59017825 275 CODE:
276 if (!SvPOK(package_name))
277 croak("The constructor argument must be the name of a package");
278
6c3e69c6 279 instance = newHV();
59017825 280
1d6c978b 281 if (!hv_store(instance, "name", 4, SvREFCNT_inc_simple_NN(package_name), 0)) {
282 SvREFCNT_dec(package_name);
c86a1630 283 SvREFCNT_dec(instance);
e8d57afd 284 croak("Couldn't initialize the 'name' key, hv_store failed");
1d6c978b 285 }
59017825 286 namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
1d6c978b 287 nsref = newRV_inc((SV*)namespace);
288 if (!hv_store(instance, "namespace", 9, nsref, 0)) {
289 SvREFCNT_dec(nsref);
c86a1630 290 SvREFCNT_dec(instance);
e8d57afd 291 croak("Couldn't initialize the 'namespace' key, hv_store failed");
1d6c978b 292 }
59017825 293
6c3e69c6 294 RETVAL = sv_bless(newRV_noinc((SV*)instance), gv_stashpv(class, 0));
59017825 295 OUTPUT:
296 RETVAL
194acf47 297
298SV*
299name(self)
300 SV *self
3496f1e8 301 PREINIT:
194acf47 302 SV **slot;
303 CODE:
304 if (!sv_isobject(self))
305 croak("Can't call name as a class method");
306 slot = hv_fetch((HV*)SvRV(self), "name", 4, 0);
a2fec41a 307 RETVAL = slot ? SvREFCNT_inc_simple_NN(*slot) : &PL_sv_undef;
194acf47 308 OUTPUT:
309 RETVAL
310
311SV*
312namespace(self)
313 SV *self
3496f1e8 314 PREINIT:
194acf47 315 SV **slot;
316 CODE:
317 if (!sv_isobject(self))
318 croak("Can't call namespace as a class method");
319 slot = hv_fetch((HV*)SvRV(self), "namespace", 9, 0);
a2fec41a 320 RETVAL = slot ? SvREFCNT_inc_simple_NN(*slot) : &PL_sv_undef;
194acf47 321 OUTPUT:
322 RETVAL
3fd56b4d 323
324void
76f7c306 325add_package_symbol(self, variable, initial=NULL, ...)
326 SV *self
327 varspec_t variable
328 SV *initial
329 PREINIT:
330 SV *name;
331 GV *glob;
332 CODE:
333 if (initial && !_valid_for_type(initial, variable.type))
334 croak("%s is not of type %s",
335 SvPV_nolen(initial), vartype_to_string(variable.type));
336
337 name = newSVsv(_get_name(self));
338 sv_catpvs(name, "::");
339 sv_catpv(name, variable.name);
340
341 /* XXX: come back to this when i feel like reimplementing caller() */
342/*
343 my $filename = $opts{filename};
344 my $first_line_num = $opts{first_line_num};
345
346 (undef, $filename, $first_line_num) = caller
347 if not defined $filename;
348
349 my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
350
351 # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
352 $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
353*/
354/*
355 if (items > 2 && (PL_perldb & 0x10) && variable.type == VAR_CODE) {
356 int i;
357 char *filename = NULL, *name;
358 I32 first_line_num, last_line_num;
359
360 if ((items - 3) % 2)
361 croak("add_package_symbol: Odd number of elements in %%opts");
362
363 for (i = 3; i < items; i += 2) {
364 char *key;
365 key = SvPV_nolen(ST(i));
366 if (strEQ(key, "filename")) {
367 if (!SvPOK(ST(i + 1)))
368 croak("add_package_symbol: filename must be a string");
369 filename = SvPV_nolen(ST(i + 1));
370 }
371 else if (strEQ(key, "first_line_num")) {
372 if (!SvIOK(ST(i + 1)))
373 croak("add_package_symbol: first_line_num must be an integer");
374 first_line_num = SvIV(ST(i + 1));
375 }
376 else if (strEQ(key, "last_line_num")) {
377 if (!SvIOK(ST(i + 1)))
378 croak("add_package_symbol: last_line_num must be an integer");
379 last_line_num = SvIV(ST(i + 1));
380 }
381 }
382
383 if (!filename) {
384 }
385 }
386*/
387
fedea7f9 388 glob = gv_fetchsv(name, GV_ADDMULTI, vartype_to_svtype(variable.type));
76f7c306 389
390 if (initial) {
391 SV *val;
392
393 if (SvROK(initial)) {
394 val = SvRV(initial);
a2fec41a 395 SvREFCNT_inc_simple_void_NN(val);
76f7c306 396 }
397 else {
398 val = newSVsv(initial);
399 }
400
401 switch (variable.type) {
402 case VAR_SCALAR:
a2fec41a 403 SvREFCNT_dec(GvSV(glob));
76f7c306 404 GvSV(glob) = val;
405 break;
406 case VAR_ARRAY:
6cce0a02 407 SvREFCNT_dec(GvAV(glob));
76f7c306 408 GvAV(glob) = (AV*)val;
409 break;
410 case VAR_HASH:
6cce0a02 411 SvREFCNT_dec(GvHV(glob));
76f7c306 412 GvHV(glob) = (HV*)val;
413 break;
414 case VAR_CODE:
6cce0a02 415 SvREFCNT_dec(GvCV(glob));
76f7c306 416 GvCV(glob) = (CV*)val;
417 break;
418 case VAR_IO:
6cce0a02 419 SvREFCNT_dec(GvIO(glob));
76f7c306 420 GvIOp(glob) = (IO*)val;
421 break;
422 }
423 }
424
5bb2b07b 425 SvREFCNT_dec(name);
426
76f7c306 427void
3fd56b4d 428remove_package_glob(self, name)
429 SV *self
430 char *name
3fd56b4d 431 CODE:
432 hv_delete(_get_namespace(self), name, strlen(name), G_DISCARD);
9b608466 433
34805376 434int
435has_package_symbol(self, variable)
436 SV *self
437 varspec_t variable
438 PREINIT:
439 HV *namespace;
440 SV **entry;
441 CODE:
442 namespace = _get_namespace(self);
443 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
444 if (!entry)
445 XSRETURN_UNDEF;
446
447 if (isGV(*entry)) {
448 GV *glob = (GV*)(*entry);
449 switch (variable.type) {
450 case VAR_SCALAR:
451 RETVAL = GvSV(glob) ? 1 : 0;
452 break;
453 case VAR_ARRAY:
454 RETVAL = GvAV(glob) ? 1 : 0;
455 break;
456 case VAR_HASH:
457 RETVAL = GvHV(glob) ? 1 : 0;
458 break;
459 case VAR_CODE:
460 RETVAL = GvCV(glob) ? 1 : 0;
461 break;
462 case VAR_IO:
463 RETVAL = GvIO(glob) ? 1 : 0;
464 break;
465 }
466 }
467 else {
468 RETVAL = (variable.type == VAR_CODE);
469 }
470 OUTPUT:
471 RETVAL
472
e3ad44fd 473SV*
4871b1a0 474get_package_symbol(self, variable)
e3ad44fd 475 SV *self
476 varspec_t variable
477 PREINIT:
fca4ed0c 478 SV *val;
e3ad44fd 479 CODE:
4871b1a0 480 val = _get_package_symbol(self, &variable, 0);
481 if (!val)
e3ad44fd 482 XSRETURN_UNDEF;
93c36417 483 RETVAL = newRV_inc(val);
4871b1a0 484 OUTPUT:
485 RETVAL
e3ad44fd 486
4871b1a0 487SV*
488get_or_add_package_symbol(self, variable)
489 SV *self
490 varspec_t variable
491 PREINIT:
492 SV *val;
493 CODE:
494 val = _get_package_symbol(self, &variable, 1);
fca4ed0c 495 if (!val)
496 XSRETURN_UNDEF;
93c36417 497 RETVAL = newRV_inc(val);
e3ad44fd 498 OUTPUT:
499 RETVAL
500
9b608466 501void
215f49f8 502remove_package_symbol(self, variable)
503 SV *self
504 varspec_t variable
505 PREINIT:
506 HV *namespace;
507 SV **entry;
508 CODE:
509 namespace = _get_namespace(self);
510 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
511 if (!entry)
512 XSRETURN_EMPTY;
513
514 if (isGV(*entry)) {
515 GV *glob = (GV*)(*entry);
516 switch (variable.type) {
517 case VAR_SCALAR:
4e3b237e 518 GvSV(glob) = (SV *)NULL;
215f49f8 519 break;
520 case VAR_ARRAY:
4e3b237e 521 GvAV(glob) = (AV *)NULL;
215f49f8 522 break;
523 case VAR_HASH:
4e3b237e 524 GvHV(glob) = (HV *)NULL;
215f49f8 525 break;
526 case VAR_CODE:
4e3b237e 527 GvCV(glob) = (CV *)NULL;
215f49f8 528 break;
529 case VAR_IO:
4e3b237e 530 GvIOp(glob) = (IO *)NULL;
215f49f8 531 break;
532 }
533 }
534 else {
535 if (variable.type == VAR_CODE) {
536 hv_delete(namespace, variable.name, strlen(variable.name), G_DISCARD);
537 }
538 }
539
540void
9b608466 541list_all_package_symbols(self, vartype=VAR_NONE)
542 SV *self
543 vartype_t vartype
544 PPCODE:
545 if (vartype == VAR_NONE) {
546 HV *namespace;
547 HE *entry;
548 int keys;
549
550 namespace = _get_namespace(self);
551 keys = hv_iterinit(namespace);
552 EXTEND(SP, keys);
ebcc8ba8 553 while ((entry = hv_iternext(namespace))) {
9b608466 554 mPUSHs(newSVhek(HeKEY_hek(entry)));
555 }
556 }
557 else {
558 HV *namespace;
9b608466 559 SV *val;
560 char *key;
561 int len;
562
563 namespace = _get_namespace(self);
564 hv_iterinit(namespace);
ebcc8ba8 565 while ((val = hv_iternextsv(namespace, &key, &len))) {
9b608466 566 GV *gv = (GV*)val;
567 if (isGV(gv)) {
568 switch (vartype) {
569 case VAR_SCALAR:
570 if (GvSV(val))
571 mXPUSHp(key, len);
572 break;
573 case VAR_ARRAY:
574 if (GvAV(val))
575 mXPUSHp(key, len);
576 break;
577 case VAR_HASH:
578 if (GvHV(val))
579 mXPUSHp(key, len);
580 break;
581 case VAR_CODE:
582 if (GvCVu(val))
583 mXPUSHp(key, len);
584 break;
585 case VAR_IO:
586 if (GvIO(val))
587 mXPUSHp(key, len);
588 break;
589 }
590 }
591 else if (vartype == VAR_CODE) {
592 mXPUSHp(key, len);
593 }
594 }
595 }