ff8cf70fd2380c8a078cc34eee9f8d18ee782a84
[gitmo/Package-Stash-XS.git] / Stash.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 typedef 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
16 typedef struct {
17     vartype_t type;
18     char sigil;
19     char *name;
20 } varspec_t;
21
22 const 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
40 I32 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
58 vartype_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
80 void _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
113 void _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);
123     SAVEFREEPV(varspec->name);
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
138 int _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:
156         return sv_type == SVt_PVIO;
157     default:
158         return 0;
159     }
160 }
161
162 HV *_get_namespace(SV *self)
163 {
164     dSP;
165     SV *ret;
166
167     PUSHMARK(SP);
168     XPUSHs(self);
169     PUTBACK;
170
171     call_method("namespace", G_SCALAR);
172
173     SPAGAIN;
174     ret = POPs;
175     PUTBACK;
176
177     return (HV*)SvRV(ret);
178 }
179
180 SV *_get_name(SV *self)
181 {
182     dSP;
183     SV *ret;
184
185     PUSHMARK(SP);
186     XPUSHs(self);
187     PUTBACK;
188
189     call_method("name", G_SCALAR);
190
191     SPAGAIN;
192     ret = POPs;
193     PUTBACK;
194
195     return ret;
196 }
197
198 MODULE = Package::Stash  PACKAGE = Package::Stash
199
200 PROTOTYPES: DISABLE
201
202 SV*
203 new(class, package_name)
204     char *class
205     SV *package_name
206   PREINIT:
207     HV *instance;
208     HV *namespace;
209   CODE:
210     if (!SvPOK(package_name))
211         croak("The constructor argument must be the name of a package");
212
213     instance = newHV();
214
215     hv_store(instance, "name", 4, package_name, 0);
216     namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
217     hv_store(instance, "namespace", 9, newRV((SV*)namespace), 0);
218
219     RETVAL = sv_bless(newRV((SV*)instance), gv_stashpv(class, 0));
220   OUTPUT:
221     RETVAL
222
223 SV*
224 name(self)
225     SV *self
226   PREINIT:
227     SV **slot;
228   CODE:
229     if (!sv_isobject(self))
230         croak("Can't call name as a class method");
231     slot = hv_fetch((HV*)SvRV(self), "name", 4, 0);
232     RETVAL = slot ? SvREFCNT_inc(*slot) : &PL_sv_undef;
233   OUTPUT:
234     RETVAL
235
236 SV*
237 namespace(self)
238     SV *self
239   PREINIT:
240     SV **slot;
241   CODE:
242     if (!sv_isobject(self))
243         croak("Can't call namespace as a class method");
244     slot = hv_fetch((HV*)SvRV(self), "namespace", 9, 0);
245     RETVAL = slot ? SvREFCNT_inc(*slot) : &PL_sv_undef;
246   OUTPUT:
247     RETVAL
248
249 void
250 add_package_symbol(self, variable, initial=NULL, ...)
251     SV *self
252     varspec_t variable
253     SV *initial
254   PREINIT:
255     SV *name;
256     GV *glob;
257   CODE:
258     if (initial && !_valid_for_type(initial, variable.type))
259         croak("%s is not of type %s",
260               SvPV_nolen(initial), vartype_to_string(variable.type));
261
262     name = newSVsv(_get_name(self));
263     sv_catpvs(name, "::");
264     sv_catpv(name, variable.name);
265
266     /* XXX: come back to this when i feel like reimplementing caller() */
267 /*
268     my $filename = $opts{filename};
269     my $first_line_num = $opts{first_line_num};
270
271     (undef, $filename, $first_line_num) = caller
272         if not defined $filename;
273
274     my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
275
276     # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
277     $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
278 */
279 /*
280     if (items > 2 && (PL_perldb & 0x10) && variable.type == VAR_CODE) {
281         int i;
282         char *filename = NULL, *name;
283         I32 first_line_num, last_line_num;
284
285         if ((items - 3) % 2)
286             croak("add_package_symbol: Odd number of elements in %%opts");
287
288         for (i = 3; i < items; i += 2) {
289             char *key;
290             key = SvPV_nolen(ST(i));
291             if (strEQ(key, "filename")) {
292                 if (!SvPOK(ST(i + 1)))
293                     croak("add_package_symbol: filename must be a string");
294                 filename = SvPV_nolen(ST(i + 1));
295             }
296             else if (strEQ(key, "first_line_num")) {
297                 if (!SvIOK(ST(i + 1)))
298                     croak("add_package_symbol: first_line_num must be an integer");
299                 first_line_num = SvIV(ST(i + 1));
300             }
301             else if (strEQ(key, "last_line_num")) {
302                 if (!SvIOK(ST(i + 1)))
303                     croak("add_package_symbol: last_line_num must be an integer");
304                 last_line_num = SvIV(ST(i + 1));
305             }
306         }
307
308         if (!filename) {
309         }
310     }
311 */
312
313     glob = gv_fetchsv(name, GV_ADD, vartype_to_svtype(variable.type));
314
315     if (initial) {
316         SV *val;
317
318         if (SvROK(initial)) {
319             val = SvRV(initial);
320             SvREFCNT_inc(val);
321         }
322         else {
323             val = newSVsv(initial);
324         }
325
326         switch (variable.type) {
327         case VAR_SCALAR:
328             GvSV(glob) = val;
329             break;
330         case VAR_ARRAY:
331             GvAV(glob) = (AV*)val;
332             break;
333         case VAR_HASH:
334             GvHV(glob) = (HV*)val;
335             break;
336         case VAR_CODE:
337             GvCV(glob) = (CV*)val;
338             break;
339         case VAR_IO:
340             GvIOp(glob) = (IO*)val;
341             break;
342         }
343     }
344
345 void
346 remove_package_glob(self, name)
347     SV *self
348     char *name
349   CODE:
350     hv_delete(_get_namespace(self), name, strlen(name), G_DISCARD);
351
352 int
353 has_package_symbol(self, variable)
354     SV *self
355     varspec_t variable
356   PREINIT:
357     HV *namespace;
358     SV **entry;
359   CODE:
360     namespace = _get_namespace(self);
361     entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
362     if (!entry)
363         XSRETURN_UNDEF;
364
365     if (isGV(*entry)) {
366         GV *glob = (GV*)(*entry);
367         switch (variable.type) {
368         case VAR_SCALAR:
369             RETVAL = GvSV(glob) ? 1 : 0;
370             break;
371         case VAR_ARRAY:
372             RETVAL = GvAV(glob) ? 1 : 0;
373             break;
374         case VAR_HASH:
375             RETVAL = GvHV(glob) ? 1 : 0;
376             break;
377         case VAR_CODE:
378             RETVAL = GvCV(glob) ? 1 : 0;
379             break;
380         case VAR_IO:
381             RETVAL = GvIO(glob) ? 1 : 0;
382             break;
383         }
384     }
385     else {
386         RETVAL = (variable.type == VAR_CODE);
387     }
388   OUTPUT:
389     RETVAL
390
391 SV*
392 get_package_symbol(self, variable, ...)
393     SV *self
394     varspec_t variable
395   PREINIT:
396     HV *namespace;
397     SV **entry;
398     GV *glob;
399     int i, vivify = 0;
400     SV *val;
401   CODE:
402     if (items > 2 && (items - 2) % 2)
403         croak("get_package_symbol: Odd number of elements in %%opts");
404
405     for (i = 2; i < items; i += 2) {
406         char *key;
407         key = SvPV_nolen(ST(i));
408         if (strEQ(key, "vivify")) {
409             vivify = SvTRUE(ST(i + 1));
410         }
411     }
412
413     namespace = _get_namespace(self);
414     entry = hv_fetch(namespace, variable.name, strlen(variable.name), vivify);
415     if (!entry)
416         XSRETURN_UNDEF;
417
418     glob = (GV*)(*entry);
419     if (!isGV(glob)) {
420         SV *namesv;
421         char *name;
422         STRLEN len;
423
424         namesv = newSVsv(_get_name(self));
425         sv_catpvs(namesv, "::");
426         sv_catpv(namesv, variable.name);
427
428         name = SvPV(namesv, len);
429
430         gv_init(glob, namespace, name, len, 1);
431     }
432
433     if (vivify) {
434         switch (variable.type) {
435         case VAR_SCALAR:
436             if (!GvSV(glob))
437                 GvSV(glob) = newSV(0);
438             break;
439         case VAR_ARRAY:
440             if (!GvAV(glob))
441                 GvAV(glob) = newAV();
442             break;
443         case VAR_HASH:
444             if (!GvHV(glob))
445                 GvHV(glob) = newHV();
446             break;
447         case VAR_CODE:
448             croak("Don't know how to vivify CODE variables");
449         case VAR_IO:
450             if (!GvIO(glob))
451                 GvIOp(glob) = newIO();
452             break;
453         default:
454             croak("Unknown type in vivication");
455         }
456     }
457
458     switch (variable.type) {
459     case VAR_SCALAR:
460         val = GvSV(glob);
461         break;
462     case VAR_ARRAY:
463         val = (SV*)GvAV(glob);
464         break;
465     case VAR_HASH:
466         val = (SV*)GvHV(glob);
467         break;
468     case VAR_CODE:
469         val = (SV*)GvCV(glob);
470         break;
471     case VAR_IO:
472         val = (SV*)GvIO(glob);
473         break;
474     }
475
476     if (!val)
477         XSRETURN_UNDEF;
478
479     RETVAL = newRV(val);
480   OUTPUT:
481     RETVAL
482
483 void
484 remove_package_symbol(self, variable)
485     SV *self
486     varspec_t variable
487   PREINIT:
488     HV *namespace;
489     SV **entry;
490   CODE:
491     namespace = _get_namespace(self);
492     entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
493     if (!entry)
494         XSRETURN_EMPTY;
495
496     if (isGV(*entry)) {
497         GV *glob = (GV*)(*entry);
498         switch (variable.type) {
499         case VAR_SCALAR:
500             GvSV(glob) = Nullsv;
501             break;
502         case VAR_ARRAY:
503             GvAV(glob) = Nullav;
504             break;
505         case VAR_HASH:
506             GvHV(glob) = Nullhv;
507             break;
508         case VAR_CODE:
509             GvCV(glob) = Nullcv;
510             break;
511         case VAR_IO:
512             GvIOp(glob) = Null(IO*);
513             break;
514         }
515     }
516     else {
517         if (variable.type == VAR_CODE) {
518             hv_delete(namespace, variable.name, strlen(variable.name), G_DISCARD);
519         }
520     }
521
522 void
523 list_all_package_symbols(self, vartype=VAR_NONE)
524     SV *self
525     vartype_t vartype
526   PPCODE:
527     if (vartype == VAR_NONE) {
528         HV *namespace;
529         HE *entry;
530         int keys;
531
532         namespace = _get_namespace(self);
533         keys = hv_iterinit(namespace);
534         EXTEND(SP, keys);
535         while (entry = hv_iternext(namespace)) {
536             mPUSHs(newSVhek(HeKEY_hek(entry)));
537         }
538     }
539     else {
540         HV *namespace;
541         HE *entry;
542         SV *val;
543         char *key;
544         int len;
545
546         namespace = _get_namespace(self);
547         hv_iterinit(namespace);
548         while (val = hv_iternextsv(namespace, &key, &len)) {
549             GV *gv = (GV*)val;
550             if (isGV(gv)) {
551                 switch (vartype) {
552                 case VAR_SCALAR:
553                     if (GvSV(val))
554                         mXPUSHp(key, len);
555                     break;
556                 case VAR_ARRAY:
557                     if (GvAV(val))
558                         mXPUSHp(key, len);
559                     break;
560                 case VAR_HASH:
561                     if (GvHV(val))
562                         mXPUSHp(key, len);
563                     break;
564                 case VAR_CODE:
565                     if (GvCVu(val))
566                         mXPUSHp(key, len);
567                     break;
568                 case VAR_IO:
569                     if (GvIO(val))
570                         mXPUSHp(key, len);
571                     break;
572                 }
573             }
574             else if (vartype == VAR_CODE) {
575                 mXPUSHp(key, len);
576             }
577         }
578     }