explicitly don't pass args to the accessors
[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 | G_NOARGS);
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 | G_NOARGS);
190
191     SPAGAIN;
192     ret = POPs;
193     PUTBACK;
194
195     return ret;
196 }
197
198 SV *_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;
212
213         namesv = newSVsv(_get_name(self));
214         sv_catpvs(namesv, "::");
215         sv_catpv(namesv, variable->name);
216
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));
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
263 MODULE = Package::Stash  PACKAGE = Package::Stash
264
265 PROTOTYPES: DISABLE
266
267 SV*
268 new(class, package_name)
269     char *class
270     SV *package_name
271   PREINIT:
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
287
288 SV*
289 name(self)
290     SV *self
291   PREINIT:
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
301 SV*
302 namespace(self)
303     SV *self
304   PREINIT:
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
313
314 void
315 add_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
378     glob = gv_fetchsv(name, GV_ADDMULTI, vartype_to_svtype(variable.type));
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
410 void
411 remove_package_glob(self, name)
412     SV *self
413     char *name
414   CODE:
415     hv_delete(_get_namespace(self), name, strlen(name), G_DISCARD);
416
417 int
418 has_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
456 SV*
457 get_package_symbol(self, variable)
458     SV *self
459     varspec_t variable
460   PREINIT:
461     SV *val;
462   CODE:
463     val = _get_package_symbol(self, &variable, 0);
464     if (!val)
465         XSRETURN_UNDEF;
466     RETVAL = newRV(val);
467   OUTPUT:
468     RETVAL
469
470 SV*
471 get_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);
478     if (!val)
479         XSRETURN_UNDEF;
480     RETVAL = newRV(val);
481   OUTPUT:
482     RETVAL
483
484 void
485 remove_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:
513             GvIOp(glob) = Null(IO*);
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
523 void
524 list_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     }