explicitly don't pass args to the accessors
[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;
116 char *type;
117
118 val = hv_fetch(variable, "name", 4, 0);
119 if (!val)
120 croak("The 'name' key is required in variable specs");
121
122 varspec->name = savesvpv(*val);
301f570b 123 SAVEFREEPV(varspec->name);
a382a84b 124
125 val = hv_fetch(variable, "sigil", 5, 0);
126 if (!val)
127 croak("The 'sigil' key is required in variable specs");
128
129 varspec->sigil = (SvPV_nolen(*val))[0];
130
131 val = hv_fetch(variable, "type", 4, 0);
132 if (!val)
133 croak("The 'type' key is required in variable specs");
134
135 varspec->type = string_to_vartype(SvPV_nolen(*val));
136}
137
138int _valid_for_type(SV *value, vartype_t type)
139{
140 svtype sv_type = SvROK(value) ? SvTYPE(SvRV(value)) : SVt_NULL;
141
142 switch (type) {
143 case VAR_SCALAR:
144 return sv_type == SVt_NULL ||
145 sv_type == SVt_IV ||
146 sv_type == SVt_NV ||
147 sv_type == SVt_PV ||
148 sv_type == SVt_RV;
149 case VAR_ARRAY:
150 return sv_type == SVt_PVAV;
151 case VAR_HASH:
152 return sv_type == SVt_PVHV;
153 case VAR_CODE:
154 return sv_type == SVt_PVCV;
155 case VAR_IO:
76f7c306 156 return sv_type == SVt_PVIO;
a382a84b 157 default:
158 return 0;
159 }
160}
161
3fd56b4d 162HV *_get_namespace(SV *self)
163{
164 dSP;
165 SV *ret;
166
167 PUSHMARK(SP);
168 XPUSHs(self);
169 PUTBACK;
170
a84abffa 171 call_method("namespace", G_SCALAR | G_NOARGS);
3fd56b4d 172
173 SPAGAIN;
174 ret = POPs;
175 PUTBACK;
176
177 return (HV*)SvRV(ret);
178}
179
76f7c306 180SV *_get_name(SV *self)
181{
182 dSP;
183 SV *ret;
184
185 PUSHMARK(SP);
186 XPUSHs(self);
187 PUTBACK;
188
a84abffa 189 call_method("name", G_SCALAR | G_NOARGS);
76f7c306 190
191 SPAGAIN;
192 ret = POPs;
193 PUTBACK;
194
195 return ret;
196}
197
4871b1a0 198SV *_get_package_symbol(SV *self, varspec_t *variable, int vivify)
199{
200 HV *namespace;
201 SV **entry;
202 GV *glob;
203
204 namespace = _get_namespace(self);
205 entry = hv_fetch(namespace, variable->name, strlen(variable->name), vivify);
206 if (!entry)
207 return NULL;
208
209 glob = (GV*)(*entry);
210 if (!isGV(glob)) {
211 SV *namesv;
4871b1a0 212
213 namesv = newSVsv(_get_name(self));
214 sv_catpvs(namesv, "::");
215 sv_catpv(namesv, variable->name);
216
b55c97c2 217 /* can't use gv_init here, because it screws up @ISA in a way that I
218 * can't reproduce, but that CMOP triggers */
219 gv_fetchsv(namesv, GV_ADD, vartype_to_svtype(variable->type));
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;
274 CODE:
275 if (!SvPOK(package_name))
276 croak("The constructor argument must be the name of a package");
277
278 instance = newHV();
279
280 hv_store(instance, "name", 4, package_name, 0);
281 namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
282 hv_store(instance, "namespace", 9, newRV((SV*)namespace), 0);
283
284 RETVAL = sv_bless(newRV((SV*)instance), gv_stashpv(class, 0));
285 OUTPUT:
286 RETVAL
194acf47 287
288SV*
289name(self)
290 SV *self
3496f1e8 291 PREINIT:
194acf47 292 SV **slot;
293 CODE:
294 if (!sv_isobject(self))
295 croak("Can't call name as a class method");
296 slot = hv_fetch((HV*)SvRV(self), "name", 4, 0);
297 RETVAL = slot ? SvREFCNT_inc(*slot) : &PL_sv_undef;
298 OUTPUT:
299 RETVAL
300
301SV*
302namespace(self)
303 SV *self
3496f1e8 304 PREINIT:
194acf47 305 SV **slot;
306 CODE:
307 if (!sv_isobject(self))
308 croak("Can't call namespace as a class method");
309 slot = hv_fetch((HV*)SvRV(self), "namespace", 9, 0);
310 RETVAL = slot ? SvREFCNT_inc(*slot) : &PL_sv_undef;
311 OUTPUT:
312 RETVAL
3fd56b4d 313
314void
76f7c306 315add_package_symbol(self, variable, initial=NULL, ...)
316 SV *self
317 varspec_t variable
318 SV *initial
319 PREINIT:
320 SV *name;
321 GV *glob;
322 CODE:
323 if (initial && !_valid_for_type(initial, variable.type))
324 croak("%s is not of type %s",
325 SvPV_nolen(initial), vartype_to_string(variable.type));
326
327 name = newSVsv(_get_name(self));
328 sv_catpvs(name, "::");
329 sv_catpv(name, variable.name);
330
331 /* XXX: come back to this when i feel like reimplementing caller() */
332/*
333 my $filename = $opts{filename};
334 my $first_line_num = $opts{first_line_num};
335
336 (undef, $filename, $first_line_num) = caller
337 if not defined $filename;
338
339 my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
340
341 # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
342 $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
343*/
344/*
345 if (items > 2 && (PL_perldb & 0x10) && variable.type == VAR_CODE) {
346 int i;
347 char *filename = NULL, *name;
348 I32 first_line_num, last_line_num;
349
350 if ((items - 3) % 2)
351 croak("add_package_symbol: Odd number of elements in %%opts");
352
353 for (i = 3; i < items; i += 2) {
354 char *key;
355 key = SvPV_nolen(ST(i));
356 if (strEQ(key, "filename")) {
357 if (!SvPOK(ST(i + 1)))
358 croak("add_package_symbol: filename must be a string");
359 filename = SvPV_nolen(ST(i + 1));
360 }
361 else if (strEQ(key, "first_line_num")) {
362 if (!SvIOK(ST(i + 1)))
363 croak("add_package_symbol: first_line_num must be an integer");
364 first_line_num = SvIV(ST(i + 1));
365 }
366 else if (strEQ(key, "last_line_num")) {
367 if (!SvIOK(ST(i + 1)))
368 croak("add_package_symbol: last_line_num must be an integer");
369 last_line_num = SvIV(ST(i + 1));
370 }
371 }
372
373 if (!filename) {
374 }
375 }
376*/
377
fedea7f9 378 glob = gv_fetchsv(name, GV_ADDMULTI, vartype_to_svtype(variable.type));
76f7c306 379
380 if (initial) {
381 SV *val;
382
383 if (SvROK(initial)) {
384 val = SvRV(initial);
385 SvREFCNT_inc(val);
386 }
387 else {
388 val = newSVsv(initial);
389 }
390
391 switch (variable.type) {
392 case VAR_SCALAR:
393 GvSV(glob) = val;
394 break;
395 case VAR_ARRAY:
396 GvAV(glob) = (AV*)val;
397 break;
398 case VAR_HASH:
399 GvHV(glob) = (HV*)val;
400 break;
401 case VAR_CODE:
402 GvCV(glob) = (CV*)val;
403 break;
404 case VAR_IO:
405 GvIOp(glob) = (IO*)val;
406 break;
407 }
408 }
409
410void
3fd56b4d 411remove_package_glob(self, name)
412 SV *self
413 char *name
3fd56b4d 414 CODE:
415 hv_delete(_get_namespace(self), name, strlen(name), G_DISCARD);
9b608466 416
34805376 417int
418has_package_symbol(self, variable)
419 SV *self
420 varspec_t variable
421 PREINIT:
422 HV *namespace;
423 SV **entry;
424 CODE:
425 namespace = _get_namespace(self);
426 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
427 if (!entry)
428 XSRETURN_UNDEF;
429
430 if (isGV(*entry)) {
431 GV *glob = (GV*)(*entry);
432 switch (variable.type) {
433 case VAR_SCALAR:
434 RETVAL = GvSV(glob) ? 1 : 0;
435 break;
436 case VAR_ARRAY:
437 RETVAL = GvAV(glob) ? 1 : 0;
438 break;
439 case VAR_HASH:
440 RETVAL = GvHV(glob) ? 1 : 0;
441 break;
442 case VAR_CODE:
443 RETVAL = GvCV(glob) ? 1 : 0;
444 break;
445 case VAR_IO:
446 RETVAL = GvIO(glob) ? 1 : 0;
447 break;
448 }
449 }
450 else {
451 RETVAL = (variable.type == VAR_CODE);
452 }
453 OUTPUT:
454 RETVAL
455
e3ad44fd 456SV*
4871b1a0 457get_package_symbol(self, variable)
e3ad44fd 458 SV *self
459 varspec_t variable
460 PREINIT:
fca4ed0c 461 SV *val;
e3ad44fd 462 CODE:
4871b1a0 463 val = _get_package_symbol(self, &variable, 0);
464 if (!val)
e3ad44fd 465 XSRETURN_UNDEF;
4871b1a0 466 RETVAL = newRV(val);
467 OUTPUT:
468 RETVAL
e3ad44fd 469
4871b1a0 470SV*
471get_or_add_package_symbol(self, variable)
472 SV *self
473 varspec_t variable
474 PREINIT:
475 SV *val;
476 CODE:
477 val = _get_package_symbol(self, &variable, 1);
fca4ed0c 478 if (!val)
479 XSRETURN_UNDEF;
fca4ed0c 480 RETVAL = newRV(val);
e3ad44fd 481 OUTPUT:
482 RETVAL
483
9b608466 484void
215f49f8 485remove_package_symbol(self, variable)
486 SV *self
487 varspec_t variable
488 PREINIT:
489 HV *namespace;
490 SV **entry;
491 CODE:
492 namespace = _get_namespace(self);
493 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
494 if (!entry)
495 XSRETURN_EMPTY;
496
497 if (isGV(*entry)) {
498 GV *glob = (GV*)(*entry);
499 switch (variable.type) {
500 case VAR_SCALAR:
501 GvSV(glob) = Nullsv;
502 break;
503 case VAR_ARRAY:
504 GvAV(glob) = Nullav;
505 break;
506 case VAR_HASH:
507 GvHV(glob) = Nullhv;
508 break;
509 case VAR_CODE:
510 GvCV(glob) = Nullcv;
511 break;
512 case VAR_IO:
76f7c306 513 GvIOp(glob) = Null(IO*);
215f49f8 514 break;
515 }
516 }
517 else {
518 if (variable.type == VAR_CODE) {
519 hv_delete(namespace, variable.name, strlen(variable.name), G_DISCARD);
520 }
521 }
522
523void
9b608466 524list_all_package_symbols(self, vartype=VAR_NONE)
525 SV *self
526 vartype_t vartype
527 PPCODE:
528 if (vartype == VAR_NONE) {
529 HV *namespace;
530 HE *entry;
531 int keys;
532
533 namespace = _get_namespace(self);
534 keys = hv_iterinit(namespace);
535 EXTEND(SP, keys);
536 while (entry = hv_iternext(namespace)) {
537 mPUSHs(newSVhek(HeKEY_hek(entry)));
538 }
539 }
540 else {
541 HV *namespace;
542 HE *entry;
543 SV *val;
544 char *key;
545 int len;
546
547 namespace = _get_namespace(self);
548 hv_iterinit(namespace);
549 while (val = hv_iternextsv(namespace, &key, &len)) {
550 GV *gv = (GV*)val;
551 if (isGV(gv)) {
552 switch (vartype) {
553 case VAR_SCALAR:
554 if (GvSV(val))
555 mXPUSHp(key, len);
556 break;
557 case VAR_ARRAY:
558 if (GvAV(val))
559 mXPUSHp(key, len);
560 break;
561 case VAR_HASH:
562 if (GvHV(val))
563 mXPUSHp(key, len);
564 break;
565 case VAR_CODE:
566 if (GvCVu(val))
567 mXPUSHp(key, len);
568 break;
569 case VAR_IO:
570 if (GvIO(val))
571 mXPUSHp(key, len);
572 break;
573 }
574 }
575 else if (vartype == VAR_CODE) {
576 mXPUSHp(key, len);
577 }
578 }
579 }